`include "chunjun_define.sv" 
`include "chunjun_lib_define.sv" 

//================================================================================================
// File Name   : dcu/chunjun_dcu.sv
// Create Time : Mon Sep  9 10:49:11 2024
// Description :
//  
//  DCU (DCache Control Unit) is the cache controller of level-1 dcache.
//  DCU interacts with LSU/SBU/BMU modules to handle load/store requests, 
//  checks for request hit in the L1 DCache.
//  - If a hit occurs, DCU read/write data directly in the DCache.
//  - If a miss occurs, DCU sends cacheline refill request to SoC via BMU.
// 
//================================================================================================
///* 
// ● 输入端口：
//  ○ core_priv_mode：内核当前的特权模式。
//  ○ pcu_csr_*：用于配置的控制与状态寄存器接口。
//  ○ lsu_dcu_*：与加载/存储单元（LSU）的接口，处理内存请求。
//  ○ sbu_dcu_*：与存储缓冲单元（SBU）的接口，处理存储操作。
//● 输出端口：
//  ○ dcu_lsu_*：向LSU返回数据及错误信号等响应。
//  ○ dcu_bmu_*：向BMU发起内存事务命令（AR、AW、W通道）。
//  ○ ras_valid：报告RAS错误，包括地址和上下文信息。
//*/

module chunjun_dcu (
    input  logic [1:0]                                                     core_priv_mode               , //privilege mode，用于linefill evict发送写请求和load请求, ld指令使用
    // init_busy indicates dcache initilaization complete
    output logic                                                           dcu_init_busy                , //指示dcache初始化是否完成
    // no pending requests in DCU
    output logic                                                           dcu_idle                     , //DCache没有任何pending的请求

    // Machine system configuration register
    input  logic [32-1:0]                                       pcu_csr_msyscfg                         , //bit[1]: 1'b1 - DCache功能开启；1'b0 - DCache功能关闭, bit[3]: 1'b1 - ECC功能开启；1'b0 - ECC功能关闭
    // Machine auxiliary configuration register
    input  logic [32-1:0]                                       pcu_csr_mauxcfg                         , //机器辅助配置寄存器, nouse
    // Machine subsystem ECC inject control register
    input  logic [32-1:0]                                       pcu_csr_meictl                          , //机器子系统ECC注入控制寄存器, [4:1] = 4'b0100: DCache tag, 4'b0101: DCache data. [0]:inject_in. [8:5]:select bank
    // Machine ECC inject gitmap register low
    input  logic [32-1:0]                                       pcu_csr_meibmap                         , //Machine ECC Inject Bitmap Register Low 32 bit
    // Machine ECC inject gitmap register high
    input  logic [32-1:0]                                       pcu_csr_meibmaph                        , //Machine ECC Inject Bitmap Register high 32 bit


// LSU interface
///////////////////////
// load req and addr //
///////////////////////

// load req and addr //
input  logic [1:0]                                                  lsu_dcu_ld_req_valid_m1,  //发送给DCU的Load请求有效信号2bit 分别表示两路load口，下同不赘述约束：此信号不会是2'b10（也就是没有只有1口有请求的场景）
output logic [1:0]                                                  dcu_lsu_ld_req_ready_m1,  //DCU 发送 load请求ready信号。
input  logic [1:0][32-1:0]                                          lsu_dcu_ld_req_addr_m1,  //发送给DCU的Load请求地址，4B对齐（DCU可以忽略低2bit）
input  logic [1:0][1:0]                                             lsu_dcu_ld_req_size_m1,  //发送给DCU的Load请求大小0 - Byte，1 - Half Word，2 - Word
input  logic [1:0][($clog2(6) + 1)-1:0]                             lsu_dcu_ld_req_lsqid_m1,  //发送给DCU的Load请求id最高位为绕回位低位部分为LSQ Entry index下同不赘述。
// 0: Non-idempotency (device); 1: idempotency (non-device)
input  logic [1:0]                                                  lsu_dcu_ld_req_idempotency_m1,  //0: Non-idempotency (device); 1: idempotency (non-device)
// bit[2] - 0: non-write-allocate; 1: write-allocate
// bit[1] - 0: non-read-allocate; 1: read-allocate  
// bit[0] - 0: non-cacheable; 1: cacheable
input  logic [1:0][2:0]                                             lsu_dcu_ld_req_inner_ma_m1,  //LSU告诉DCU加载请求的内部MA, 
input  logic [1:0][2:0]                                             lsu_dcu_ld_req_outer_ma_m1,  //LSU告诉DCU加载请求的外部MA, nouse

input  logic [1:0]                                                  lsu_dcu_ld_req_excl_m1,     //发送给DCU的读请求是否为排他读0：非exclusive load,1：exclusive load
input  logic [1:0]                                                  lsu_dcu_ld_req_master_m1,  //指示当前请求来源0:来自于内核1:来自于调试器
input  logic [1:0]                                                  lsu_dcu_ld_req_samecl_m1, //表示同拍发送给DCU的两笔load请求的地址落在同一个cacheline中的指示，此信号仅当两路lsu_dcu_ld_req_valid_m1均有效时才有效


//m2阶段
input  logic [1:0]                                                  lsu_dcu_ld_req_ma_update_m2, //更新M2 stage发送给DCU的 load请求memory attribute有效指示
input  logic [1:0]                                                  lsu_dcu_ld_req_idempotency_m2, //0: Non-idempotency (device); 1: idempotency (non-device)
input  logic [1:0][2:0]                                             lsu_dcu_ld_req_inner_ma_m2, //LSU告诉DCU加载请求的内部MA
input  logic [1:0][2:0]                                             lsu_dcu_ld_req_outer_ma_m2, //LSU告诉DCU加载请求的外部MA
input  logic [1:0]                                                  lsu_dcu_ld_req_cancel_m2,   //M2 Stage发送给DCU的取消load请求指示信号

///////////////////////
// load response //
///////////////////////

// 4'b0000: no replay
// 4'b0001: miss buffer full
// 4'b0010: all way lock
// 4'b0100: miss hit miss
// 4'b1000: auto wakeup

//M2
output logic [1:0][3:0]                                             dcu_lsu_ld_replay_cause_m2, //DCU告诉LSU加载请求的replay原因,4'b0000：无replay请求,4'b0001：miss buffer full,4'b0010：all way lock,4'b0100：miss hit miss.
output logic [1:0][($clog2(6) + 1)-1:0]                             dcu_lsu_ld_replay_lsqid_m2, //M2 stage DCU对LSU的replay请求的lsq id
output logic [1:0][($clog2(4) - 1):0]                               dcu_lsu_ld_replay_mbid_m2,  //针对miss hit miss和hit evict buffer的replay场景，表示M2 stage DCU对LSU的replay请求，hit到的dcache missbuf id
output logic [4-1:0]                                                dcu_lsu_mb_rls_array,       //DCU指示LSU当前miss buffer释放的array, 

output logic [1:0]                                                  dcu_lsu_ld_rsp_valid_m2,    //DCU在M2 stage返回的Load数据响应有效信号在dcu_lsu_ld_replay_cause_m2非0时，即使m2发现load hit，也不会返回响应。
input  logic [1:0]                                                  lsu_dcu_ld_rsp_ready_m2,    //LSU接收DCU返回的读数据响应ready信号当前版本固接1，不反压DCU的读数据响应
output logic [1:0][31:0]                                            dcu_lsu_ld_rsp_data_m2,     //DCU返回的Load数据
output logic [1:0][($clog2(6) + 1)-1:0]                             dcu_lsu_ld_rsp_lsqid_m2,    //DCU返回的Load数据对应的请求id
output logic [1:0]                                                  dcu_lsu_ld_rsp_buserr_m2,   //DCU返回的Load响应出现总线响应错误指示
output logic [1:0]                                                  dcu_lsu_ld_rsp_excl_okay_m2, //DCU返回的排他load成功响应1：成功,0：失败注：此信号仅对应排他load有效

output logic [1:0]                                                  dcu_lsu_ld_rsp_valid_m3, //DCU在M3 stage返回的Load数据响应有效信号注意：仅在支持ECC，并且M2 stage hit或者出现tag ecc error时才会出现M3 stage的响应。todo
output logic [1:0]                                                  dcu_lsu_ld_rsp_raserr_m3, //DCU返回的Load响应出现RAS错误指示（不可恢复的ECC错误）
output logic [1:0]                                                  dcu_lsu_ld_rsp_replay_m3, //dcu要求lsu replay read请求的原因，one-hot信号1'b0：无replay请求1'b1：因ECC错误纠错引发的replay，这种replay自动唤醒
output logic [1:0][($clog2(6) + 1)-1:0]                             dcu_lsu_ld_rsp_lsqid_m3, //M3 stage DCU对LSU的replay请求或raserr的lsq id


input  logic                                                        lsu_dcu_flush_valid, //LSU flush有效信号，为1指示lsu需要将flush id对应的DCache中的load及更新的load请求flush掉。Flush同拍的新发送的load请求不受flush影响。
input  logic [($clog2(6) + 1)-1:0]                                  lsu_dcu_flush_lsqid, //flush id
input  logic                                                        lsu_dcu_prefetch_valid, //指示DCache预取请求有效
input  logic                                                        lsu_dcu_prefetch_write, //0：prefetch.r 1：prefetch.w. nouse
input  logic [32-1:0]                                               lsu_dcu_prefetch_addr, //发送给DCU的prefetch请求地址（默认32B对齐，DCU可以忽略低5bit）
input  logic [2:0]                                                  lsu_dcu_prefetch_outer_ma, //发送给DCU的prefetch请求的outer memory attribute，通过 bmu_cache 汇报给总线


///////////////////////
// store req and addr //
///////////////////////


// Tag Request Interface (M1 Stage)
input  logic                                                     sbu_dcu_tag_req_vld_m1, //SBU向Dcache发送写请求valid指示信号
output logic                                                     dcu_sbu_tag_req_rdy_m1, //Dcache接收SBU发出写请求ready指示信号
input  logic [32-1:0]                                            sbu_dcu_tag_req_addr_m1, //SBU向Dcache发送写请求的地址信息当前版本SBU按照size对齐的方式发送地址给dcache，通过wstrb区分有效写数据
input  logic [$clog2(6)-1:0]                                     sbu_dcu_tag_req_sbqid_m1, //SBU向dcache发出写请求的ID信息
input  logic                                                     sbu_dcu_tag_req_excl_m1, //sbu向dcu发出写请求的排他属性1'b0：非exclusive写请求1'b1：exclusive写请求*说明：目前nouse
// 0: Non-idempotency (device); 1: idempotency (non-device)
input  logic                                                     sbu_dcu_tag_req_idempotency_m1, //0: Non-idempotency (device); 1: idempotency (non-device)

// Memory Attributes (MA)
// bit[2] - 0: non-write-allocate; 1: write-allocate
// bit[1] - 0: non-read-allocate; 1: read-allocate
// bit[0] - 0: non-cacheable; 1: cacheable
input  logic [2:0]                                               sbu_dcu_tag_req_inner_ma_m1, //SBU发送给DCU的store请求的inner memory attributebit[2] - 1'b0: No-Write-Allocate; 1'b1: Write-Allocatebit[1] - 1'b0: No-Read-Allocate; 1'b1: Read-Allocate，SBU只发出写请求，DCU可忽略此bitbit[0] - 1'b0: Non-Cacheable; 1'b1: Cacheable，这里tie1，固定为cachebale
input  logic [2:0]                                               sbu_dcu_tag_req_outer_ma_m1, //SBU发送给DCU的store请求的outer memory attributebit[2] - 1'b0: No-Write-Allocate; 1'b1: Write-Allocatebit[1] - 1'b0: No-Read-Allocate; 1'b1: Read-Allocate，SBU只发出写请求，DCU可忽略此bitbit[0] - 1'b0: Non-Cacheable; 1'b1: Cacheable
input  logic                                                     sbu_dcu_tag_req_priv_m1, //1: priviledged  0:unpriviledged
input  logic                                                     sbu_dcu_tag_req_master_m1, //指示当前请求来源，0:来自于内核1:来自于调试器

// Cache Maintenance Operation (CMO)
// 3'b0000: Non-CMO
// 3'b0001: CBO.INVAL (Invalidate)
// 3'b0010: CBO.CLEAN
// 3'b0011: CBO.FLUSH
// 3'b101: invalidate all dcache
// 3'b110: clean all dcache
// 3'b111: flush all dcache
input  logic [2:0]                                               sbu_dcu_tag_req_cmoop_m1, //3'b000：非CMO请求,3'b001：CBO.INVAL,3'b010：CBO.CLEAN,3'b011：CBO.FLUSH,3'b101：Invalidate all dcache,3'b110：Clean all dcache,3'b111：Flush all dcache

// Tag Response Interface (M2 Stage)
output logic                                                     dcu_sbu_tag_rsp_vld_m2, //DCU tag响应vld信号
output logic                                                     dcu_sbu_tag_rsp_hit_m2, //dcu给sbu的tag hit指示0: cache miss1: cache hit
output logic                                                     dcu_sbu_tag_rsp_status_m2, //DCU给SBU返回此次check的tag status1'b1: tag dirty1'b0: tag clean
output logic [$clog2(4)-1:0]                                     dcu_sbu_tag_rsp_way_m2, //D若tag hit，dcu会告知hit了哪个way；如果miss，dcu会告知哪个way做替换
output logic [$clog2(6)-1:0]                                     dcu_sbu_tag_rsp_sbqid_m2, //M2 stage DCU对SBU的返回hit/miss/replay信息时的M-bus SBQ id
output logic                                                     dcu_sbu_tag_rsp_excl_ok_m2, //tag阶段排他写请求结果1'b0: 排他写失败1'b1: 排他写成功
output logic                                                     dcu_sbu_tag_rsp_raserr_m2, //Tag响应ras error信号*当ecc 2bit错误时置1。当不支持ECC时，tie 0。
output logic [4:0]                                               dcu_sbu_tag_replay_cause_m2, //dcu要求sbu replay的write请求原因，one-hot信号，仅对需要replay的请求有效.5'b00000：无replay请求, 5'b00001：miss buffer full, 5'b00010：all way lock, 5'b00100：miss hit miss, 5'b01000：hit evict buffer, 5'b10000：tag check ecc err
output logic [$clog2(4)-1:0]                                     dcu_sbu_tag_replay_mbid_m2, //M2 stage DCU对SBU的replay请求的dcache missbuf id *说明：仅在replay原因为miss hit miss或hit evict buffer时有效
output logic                                                     dcu_sbu_cmo_done, //1'b1: 指示先前发送的CMO请求已经完成

// Fill Done Signals
output logic                                                     dcu_sbu_filldone, //dcu完成cache的refill指示信号，仅对已经miss的tag请求有效表示 dcache linefill done
output logic                                                     dcu_sbu_filldone_err, //仅对已经miss的tag请求有效，表示dcache linefill done with error
output logic [$clog2(6)-1:0]                                     dcu_sbu_filldone_sbqid, //dcu_sbu_filldone有效时对应的SBU entry id


// 5'b00000: no replay
// 5'b00001: miss buffer full
// 5'b00010: all way lock
// 5'b00100: miss hit miss
// 5'b01000: hit evict buffer
// 5'b10000: tag check ecc error
//wake up
output logic [4-1:0]                                                  dcu_sbu_mb_rls_array, //DCU指示SBU当前有miss buffer释放的bitmap, 用于 miss hit miss_buffer
output logic [4-1:0]                                                  dcu_sbu_eb_done_array, //DCU指示当前有evict buffer entry完成的bitmap，用于"hit evict buffer"的唤醒匹配,用于 miss hit evict_buffer

// SBU-DCU Data Request Interface (M1 Stage)
input  logic                                                           sbu_dcu_data_req_vld_m1, //SBU对DCU进行读/写请求指示信号,仅在sbu_dcu_tag阶段握手且响应hit之后，SBU才会发送sbu_dcu_data_req_vld_m1
output logic                                                           dcu_sbu_data_req_rdy_m1, //DCU对SBU的读/写请求rdy
input  logic                                                           sbu_dcu_data_req_type_m1, //1'b0: read 1'b1: write
input  logic                                                           sbu_dcu_data_req_status_m1, //此信号表示此次data阶段请求的tag状态，由tag阶段获得1'b1: tag dirty1'b0: tag clean
input  logic [$clog2(4)-1:0]                                          sbu_dcu_data_req_way_m1, //sbu向dcu发送的读/写请求对应的cache way
input  logic [32-1:0]                                                  sbu_dcu_data_req_addr_m1, //sbu向dcu发送的读/写请求对应的cache addr
// req_type 0: read; 1: write
input  logic [1:0]                                                     sbu_dcu_data_req_mask_m1, //读改写粒度是32bit，表示sbu向dcu发送读请求的size, 2'b00 reserved, 2'b10 表示读高32 bit数据, 2'b01 表示读低32 bit数据, 2'b11 表示读64bit数据. todo
input  logic                                                           sbu_dcu_data_req_excl_m1, //请求的exclusive属性，仅当写请求时有效，读请求固定为1'b0, nouse
input  logic [63:0]                                                    sbu_dcu_data_req_wdata_m1, //SBU发出的写数据信号，仅当写请求时有效
input  logic [1:0][6:0]                                                sbu_dcu_data_req_ecc_m1, //SBU写入data对应的的ecc，仅当写请求时有效（当ECC宏关闭时，此信号tie 0）*说明：每32bit data对应7bit ecc code
input  logic [7:0]                                                     sbu_dcu_data_req_wstrb_m1, //SBU告诉DCU数据请求的wstrb,SBU向dcache发出写数据的strobe信号，每bit对应1Byte写数据，仅当写请求时有效

// req_mask indicate read size
// 2'b00: reserved; 2'b10: read high 32-bit; 2'01: read low 32-bit; 2'b11: read 64-bit data
input  logic [$clog2(6)-1:0]                                           sbu_dcu_data_req_sbqid_m1, //读/写请求id
input  logic                                                           sbu_dcu_data_req_zero_m1, //S表示CMO：CBO.ZERO此时DCU忽略wdata，将SBU指示的一条cacheline都写0

// DCU-SBU Data Response Interface (M3 Stage)
output logic                                                           dcu_sbu_data_rsp_vld_m3, //dcu读/写响应vld
output logic [63:0]                                                    dcu_sbu_data_rsp_rdata_m3, //从dcu读回的数据，仅当读请求时有效
output logic                                                           dcu_sbu_data_rsp_raserr_m3, //读响应ras error信号*当ecc 2bit错误时置1。当不支持ECC时，tie 0。
output logic [$clog2(6)-1:0]                                           dcu_sbu_data_rsp_sbqid_m3, //读/写响应id

// Waylock Signals
// why way_lock width is 6bit(miss_buf and SBQ) 
input  logic [6-1:0]                                                   sbu_dcu_waylock_vld, //cache way lock有效信号lock的场景有：cache hit之后；wake_up之后
input  logic [6-1:0][($clog2(4)-1):0]                                  sbu_dcu_waylock_way, //被lock的cache way
input  logic [6-1:0][`CHUNJUN_DC_SETIDX_W-1:0]                         sbu_dcu_waylock_index, //SBU告诉DCUwaylock请求的index被lock的cache way index


///////////////////////
// AXI Interface //
///////////////////////

// DCU-BMU AXI Interface

// Read Address Channel (AR)
output logic                                                           dcu_bmu_ar_vld, //dcu向BMU发送读请求的vld指示信号
input  logic                                                           bmu_dcu_ar_rdy, //BMU向dcu返回读请求的ready指示信号
output logic [4-1:0]                                                   dcu_bmu_ar_id, //dcu向BMU发送读请求的ID信息，用于支持乱序传输
output logic [32-1:0]                                                  dcu_bmu_ar_addr, //dcu向BMU发送读请求的地址
output logic [2:0]                                                     dcu_bmu_ar_size, //DCU告诉BMU读地址请求的大小
output logic [7:0]                                                     dcu_bmu_ar_len, //000: 1 times ,001: 2times ,010: 4times, 011: 8times 其余编码reserved
output logic [1:0]                                                     dcu_bmu_ar_burst, //00: FIXED,01: INCR,10: WRAP,11: RESERVED，支持INCR、WARP传输
output logic                                                           dcu_bmu_ar_lock, //0: Non-Exclusive accesses 1: Exclusive accesses, 原子操作：在不需要 refill 时把 lock 置为 1
output logic [3:0]                                                     dcu_bmu_ar_cache, //bit0: Bufferablebit,1: cacheable还是non-cacheable,2/3: 读分配和写分配  ,Other Allocate
output logic [2:0]                                                     dcu_bmu_ar_prot, //bit0: 0:Unprivileged 1:Privileged, bit,1: 0:Secure  1:Non-Secure, bit2: 0:Data access 1: Instruction access
output logic                                                           dcu_bmu_ar_master, //指示当前请求来源，0:来自于内核1:来自于调试器

// Read Data Channel (R)
input  logic                                                           bmu_dcu_r_vld, //BMU向dcu发送读数据的valid指示信号
output logic                                                           dcu_bmu_r_rdy, //dcu向BMU返回读数据的ready指示信号，支持反压
input  logic [4-1:0]                                                   bmu_dcu_r_id, //BBMU向dcu返回读传输ID信号，用于支持乱序传输
input  logic [`CHUNJUN_MBUS_DATA_W-1:0]                               bmu_dcu_r_data, //BMU向dcu发送读数据
input  logic [1:0]                                                     bmu_dcu_r_resp, //BMU告诉DCU读数据请求的响应，00:OKAY; 01:EXOKAY; 10:SLVERR; 11:DECERR
input  logic                                                           bmu_dcu_r_last, //BMU告诉DCU读数据请求是否是最后一个数据，1:是最后一个数据; 0:不是最后一个数据

// Write Address Channel (AW)
output logic                                                           dcu_bmu_aw_vld, //dcu向BMU发送写请求的valid指示信号
input  logic                                                           bmu_dcu_aw_rdy, //BMU向dcu返回写请求的ready指示信号
output logic [4-1:0]                                                   dcu_bmu_aw_id, //dcu向BMU发送写请求的ID信息，用于支持乱序传输
output logic [32-1:0]                                                  dcu_bmu_aw_addr, //dcu向BMU发送写请求的地址
output logic [2:0]                                                     dcu_bmu_aw_size, //000: 1byte, 001: 2byte, 010: 4byte 011: 8byte
output logic [7:0]                                                     dcu_bmu_aw_len, //BURST_LENGTH=awlen+1。
output logic [1:0]                                                     dcu_bmu_aw_burst, //00: FIXED01: INCR10: WRAP11: RESERVED
output logic                                                           dcu_bmu_aw_lock, //DCU告诉BMU写地址请求的lock，0:exclusive; 1:non-exclusive
output logic [3:0]                                                     dcu_bmu_aw_cache, //bit0: Bufferablebit1: Modifiablebit2: Allocatebit3: Other Allocate
output logic [2:0]                                                     dcu_bmu_aw_prot, //bit0: 0:Unprivileged 1:Privilegedbit1: 0:Secure  1:Non-Securebit2: 0:Data access 1: Instruction access
output logic                                                           dcu_bmu_aw_master, //指示当前请求来源，0:来自于内核1:来自于调试器
output logic [5:0]                                                     dcu_bmu_aw_atop, //Atomic操作指示信号，全0时为非Atomic请求。硬件原子.tie0
// Write Data Channel (W)
output logic                                                           dcu_bmu_w_vld, //dcu向BMU发送写数据的valid指示信号
input  logic                                                           bmu_dcu_w_rdy, //BMU向dcu返回写数据的ready指示信号
output logic [`CHUNJUN_MBUS_DATA_W-1:0]                               dcu_bmu_w_data, //dcu向BMU发送写数据，固定为8byte
output logic [`CHUNJUN_MBUS_DATA_W/8-1:0]                             dcu_bmu_w_strb, //dcu向BMU发送写数据byte有效位，通过strb信号指示当前wdata哪个byte有效
output logic                                                           dcu_bmu_w_last, //DCU告诉BMU写数据请求是否是最后一个数据，1:是最后一个数据; 0:不是最后一个数据

// Write Response Channel (B)
input  logic                                                           bmu_dcu_b_vld, //BMU告诉DCU写响应请求是否有效
output logic                                                           dcu_bmu_b_rdy, //DCU告诉BMU写响应请求是否准备好
input  logic [4-1:0]                                                   bmu_dcu_b_id, //BMU告诉DCU写响应请求的ID
input  logic [1:0]                                                     bmu_dcu_b_resp, //BMU告诉DCU写响应请求的响应，00:OKAY; 01:EXOKAY; 10:SLVERR; 11:DECERR

`ifdef CHUNJUN_SUPPORT_DCACHE
    // DCU RAM Tag Interface
    output logic [4-1:0]                                                   dcu_ram_tag_cs,    //DCU告诉RAM tag请求的cs，1:有效; 0:无效
    output logic [4-1:0]                                                   dcu_ram_tag_wr,    //DCU告诉RAM tag请求的wr，1:写; 0:读
    output logic [4-1:0][`CHUNJUN_DC_SETIDX_W-1:0]                         dcu_ram_tag_addr,  //DCU告诉RAM tag请求的addr
    output logic [4-1:0][`CHUNJUN_DC_TAG_RAM_DATA_W-1:0]                   dcu_ram_tag_wen,   //DCU告诉RAM tag请求的写bit有效信号
    output logic [4-1:0][`CHUNJUN_DC_TAG_RAM_DATA_W-1:0]                   dcu_ram_tag_wdata, //DCU告诉RAM tag请求的wdata，64位数据
    input  logic [4-1:0][`CHUNJUN_DC_TAG_RAM_DATA_W-1:0]                   dcu_ram_tag_rdata, //DCU告诉RAM tag请求的rdata，64位数据

    // DCU RAM Data0 Interface
    output logic [4-1:0]                                                   dcu_ram_data0_cs, //DCU告诉RAM data0请求的cs，1:有效; 0:无效
    output logic [4-1:0]                                                   dcu_ram_data0_wr, //DCU告诉RAM data0请求的wr，1:写; 0:读
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_ADDR_W-1:0]                  dcu_ram_data0_addr, //DCU告诉RAM data0请求的addr，4位地址
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data0_wen, //DCU告诉RAM data0请求的写bit有效信号
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data0_wdata, //DCU告诉RAM data0请求的wdata，64位数据
    input  logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data0_rdata, //DCU告诉RAM data0请求的rdata，64位数据

    // DCU RAM Data1 Interface
    output logic [4-1:0]                                                   dcu_ram_data1_cs, //DCU告诉RAM data1请求的cs，1:有效; 0:无效
    output logic [4-1:0]                                                   dcu_ram_data1_wr, //DCU告诉RAM data1请求的wr，1:写; 0:读
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_ADDR_W-1:0]                  dcu_ram_data1_addr, //DCU告诉RAM data1请求的addr，4位地址
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data1_wen, //DCU告诉RAM data1请求的写bit有效信号
    output logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data1_wdata, //DCU告诉RAM data1请求的wdata，64位数据
    input  logic [4-1:0][`CHUNJUN_DC_DATA_RAM_DATA_W-1:0]                  dcu_ram_data1_rdata, //DCU告诉RAM data1请求的rdata，64位数据
`endif // CHUNJUN_SUPPORT_DCACHE

// DCU RAS (Reliability, Availability, Serviceability) Interface
output logic [3-1:0]                                                   dcu_ras_valid, //模块向RAS发送Error Valid指示信号目前三路RAS上报接口中：dcu_ras_valid[0]：TAG RAM ECC Errordcu_ras_valid[1]：DATA RAM ECC Errordcu_ras_valid[2]：BMU写通道Error
output logic [3-1:0]                                                   dcu_ras_ce,   //可纠正错误 (Correctable Error)
output logic [3-1:0]                                                   dcu_ras_ued,  //上报的错误是Uncorrected Errors Defered，Tie 0
output logic [3-1:0]                                                   dcu_ras_uec,  //上报的错误是Uncorreted Errors Critical
output logic [3-1:0][1:0]                                              dcu_ras_priority, //上报的Error的优先级,tag=3,data/axi=1
output logic [3-1:0][2:0]                                              dcu_ras_tt, //上报的Error的transaction type：0: unspecified, 4: Explicit read , 5: Explicit write ,  6: Implicit read , 7: Implicit write 其余编码为reserved。
output logic [3-1:0]                                                   dcu_ras_scrub, //上报的Error是否在memory中已被修正。
output logic [3-1:0][7:0]                                              dcu_ras_ec, //上报的Error的Error Code Encodings,tag=5,data=3,axi=14
output logic [3-1:0][32-1:0]                                           dcu_ras_addr, //触发上报的Error的地址
output logic [3-1:0][3:0]                                              dcu_ras_aec, //上报的Error的辅助编码信息，其中 ldst=1，cmo=2 evict=5 pref=6
output logic [3-1:0]                                                   dcu_ras_tag, //上报的Error发生在cache的data段还是tag段，仅当Error发生在cache中时才会用到该信息。0: cache data or axib; 1: cache tag
output logic [3-1:0][3:0]                                              dcu_ras_way, //上报的Error发生在cache的哪个way，拉高对应的bit仅当Error发生在cache中时才会用到该信息。


///////////////////////
// Vector Interface //
///////////////////////

`ifdef CHUNJUN_SUPPORT_V_EXT
    // VPU-DCU Load Request Interface (M1 Stage)
    input  logic [1:0]                                                    vpu_dcu_ld_req_vld_m1, //VPU告诉DCU加载请求是否有效
    output logic [1:0]                                                    dcu_vpu_ld_req_rdy_m1, //DCU告诉VPU加载请求是否准备好
    input  logic [1:0][32-1:0]                                            vpu_dcu_ld_req_addr_m1, //VPU告诉DCU加载请求的地址,4byte对齐，DCU忽略低2位
    input  logic [1:0][1:0]                                               vpu_dcu_ld_req_size_m1, //VPU告诉DCU加载请求的大小,0 - Byte, 1 -Half Word, 2 -Word
    input  logic [1:0][($clog2(8)-1):0]                                   vpu_dcu_ld_req_vlqid_m1, //VPU告诉DCU加载请求对应的VPU Load buffer id
    input  logic [1:0]                                                    vpu_dcu_ld_req_idempotency_m1, //1'b0: Non-Idempotency(device);1'b1: Idempotency(non_device)
    input  logic [1:0][2:0]                                               vpu_dcu_ld_req_inner_ma_m1, //VPU告诉DCU加载请求的内部MA
    input  logic [1:0][2:0]                                               vpu_dcu_ld_req_outer_ma_m1, //VPU告诉DCU加载请求的外部MA
    input  logic [1:0]                                                    vpu_dcu_ld_req_master_m1, //指示当前请求来源，0:来自于内核1:来自于调试器
    input  logic                                                          vpu_dcu_ld_req_samecl_m1, //VPU告诉DCU加载请求的samecl
    input  logic [1:0]                                                    vpu_dcu_ld_req_cancel_m2, //VPU告诉DCU加载请求的取消

    // VPU-DCU Load Replay Interface (M2 Stage)
    output logic [1:0][3:0]                                               dcu_vpu_ld_replay_cause_m2, //DCU告诉VPU加载请求的replay原因
    output logic [1:0][($clog2(8)-1):0]                                   dcu_vpu_ld_replay_vlqid_m2, //DCU告诉VPU重放向量加载队列ID
    output logic [1:0][($clog2(4)-1):0]                                   dcu_vpu_ld_replay_mbid_m2, //DCU告诉VPU重放缺失缓冲区ID
    output logic [4-1:0]                                                  dcu_vpu_mb_rls_array, //DCU告诉VPU缺失缓冲区释放数组

    // VPU-DCU Load Response Interface (M2 and M3 Stages)
    output logic [1:0]                                                    dcu_vpu_ld_rsp_vld_m2, //DCU告诉VPU加载响应是否有效
    input  logic [1:0]                                                    vpu_dcu_ld_rsp_rdy_m2, //VPU告诉DCU加载响应是否准备好
    output logic [1:0][31:0]                                              dcu_vpu_ld_rsp_data_m2, //DCU告诉VPU加载响应的数据
    output logic [1:0][($clog2(8)-1):0]                                   dcu_vpu_ld_rsp_vlqid_m2, //DCU告诉VPU加载响应的VLQ ID
    output logic [1:0]                                                    dcu_vpu_ld_rsp_buserr_m2, //DCU告诉VPU加载响应的bus错误
    
    output logic [1:0]                                                    dcu_vpu_ld_rsp_vld_m3, //DCU告诉VPU加载响应是否有效
    output logic [1:0]                                                    dcu_vpu_ld_rsp_raserr_m3, //DCU告诉VPU加载响应的RAS错误
    output logic [1:0]                                                    dcu_vpu_ld_rsp_replay_m3, //DCU告诉VPU加载响应的replay,  one-hot信号,  1'b0：无replay请求,  1'b1：因ECC错误纠错引发的replay，这种replay自动唤醒
    output logic [1:0][($clog2(8)-1):0]                                   dcu_vpu_ld_rsp_vlqid_m3, //M3 stage DCU对VPU的replay请求或raserr的vlsq id
`endif // CHUNJUN_SUPPORT_V_EXT

// PMU Interface
output logic [1:0]                                                        dcu_pmu_access, //ID - 10, cache_l1_rw_accessbit[0]: LSU和DCU通路0口握手，或SBU和DCU的tag通路握手，或LSU和DCU的prefetch通路握手 , bit[1]: LSU和DCU通路1口握手
output logic [1:0]                                                        dcu_pmu_miss,   //ID - 11, cache_l1_rw_missbit[0]: tag compare得到的原始miss结果（不考虑hit evict buffer得到的等效miss） , bit[1]: 仅当load1在same-cacheline条件下miss才拉高，其他utag结果不影响该接口
// Clock and Reset
input  logic                                                               clk,
input  logic                                                               rst_n

);

// Acronym
// mb  - miss buffer
// eb  - evict buffer
// ld  - LSU load
// stc - SBU store tag check
// std - SBU store data rd/wr
// rfl - refill request to BMU
// lfl - cache linefill to DCache arbiter
// evc - evction data read request to DCache arbiter

//=================================================================================
// Arch
//=================================================================================
 
// address bits partition
//
// |--------------------|-----|-----|
//          tag          index  offs

// | DCache size | Addr for tag | Addr for index | CHUNJUN_DC_SETIDX_W | CHUNJUN_DC_TAG_W |
// | 4KB         | [31:10]      | [9:5]          | 5                   | 22               |
// | 8KB         | [31:11]      | [10:5]         | 6                   | 21               |
// | 16KB        | [31:12]      | [11:5]         | 7                   | 20               |
// | 32KB        | [31:13]      | [12:5]         | 8                   | 19               |
// | 64KB        | [31:14]      | [13:5]         | 9                   | 18               |

// tag RAM bank
//
//          |  3    |   2   |   1   |   0   |
//          | way_3 | way_2 | way_1 | way_0 |

// data RAM bank
// hint: data ram address[1:0] corresponding to original request address[4:3].
//is no match
//   RAM    |  Bank Pair 3 |  Bank Pair 2 |  Bank Pair 1 |  Bank Pair 0 |
// address  | Bank7  Bank6 | Bank5  Bank4 | Bank3  Bank2 | Bank1  Bank0 |
//  [1:0]   |              |              |              |              |
//          | way0   way0  | way3   way3  | way2   way2  | way1   way1  |
//   2'b11  | word7  word6 | word7  word6 | word7  word6 | word7  word6 |
//          |              |              |              |              |
//          | way1   way1  | way0   way0  | way3   way3  | way2   way2  |
//   2'b10  | word5  word4 | word5  word4 | word5  word4 | word5  word4 |
//          |              |              |              |              |
//          | way2   way2  | way1   way1  | way0   way0  | way3   way3  |
//   2'b01  | word3  word2 | word3  word2 | word3  word2 | word3  word2 |
//          |              |              |              |              |
//          | way3   way3  | way2   way2  | way1   way1  | way0   way0  |
//   2'b00  | word1  word0 | word1  word0 | word1  word0 | word1  word0 |
//           <---64bits---> 
//
//even bank        [223:192]      [159:128]       [ 95:64]        [31: 0]
// odd bank [255:224]      [191:160]      [127:96]       [63:32]

// DCache data RAM bank-pair for a doubleword = (cache_way + data_doubleword_offset) & 0x03
// DCache data RAM bank for a word = { ((cache_way + data_doubleword_offset) & 0x03), addr[2] }


// Rotate 4-bit value left by a offset.
// Used to calculate data RAM bank cs/en. 4-bit value to be rotated

//rotate_left_4bit = Bank Pair index
function [3:0] rotate_left_4bit ( //如果想取way2 doubleword=11（word6，word7）的数据，那么way=0100 ， offs = 2'b11， 结果为0010（pair1）
  input [3:0] way,      // input select_way
  input [1:0] offs      // bankpair index
);

  rotate_left_4bit = {4{offs==2'b11}} & {way[0]  , way[3:1]}
                   | {4{offs==2'b10}} & {way[1:0], way[3:2]}  
                   | {4{offs==2'b01}} & {way[2:0], way[3]}
                   | {4{offs==2'b00}} & way;

endfunction

//=================================================================================
// Local Parameters
//=================================================================================

localparam WAY_N       = 4;                     //Number of connected groups
localparam WAYIDX_W    = $clog2(4);             //index of way
localparam SET_N       = `CHUNJUN_DC_SET_N;     //no use
localparam SETIDX_W    = `CHUNJUN_DC_SETIDX_W;  //set index
localparam TAG_W       = `CHUNJUN_DC_TAG_W;     //tag width
localparam TAG_FULL_W  = TAG_W + 2;             //tag +  Include valid bit and dirty bit.
localparam TAG_RAM_D_W = `CHUNJUN_DC_TAG_RAM_DATA_W; //tag ram data width

// TAG RAM bits partition
// |----|-----|-----|--------------------|
//   ECC Dirty Valid         tag

localparam TAG_ECC_BASE_BIT = TAG_W + 2; //tag_ecc start index, 21bit
localparam TAG_DIRTY_BIT    = TAG_W + 1; //dirty index in tag, 20bit
localparam TAG_VALID_BIT    = TAG_W;     //valid index in tag, 19bit

localparam DAT_RAM_A_W = `CHUNJUN_DC_DATA_RAM_ADDR_W;   //10bit, addr[{13:5},{4:2}], [3:2] use to bankpir index, so addr is [{13:5},{4}]
localparam DAT_RAM_D_W = `CHUNJUN_DC_DATA_RAM_DATA_W;   //32 bit
localparam MB_ENT_N    = 4;                             // miss buf enrty
localparam MB_ID_W     = $clog2(4);                     // miss buf index
localparam LSQID_W     = ($clog2(6) + 1);               // LSQ  index , reserve bit? todo
localparam SBQID_W     = $clog2(6);                     // SBQ  index 
localparam VLQID_W     = $clog2(8);                     // VLQ  index 
localparam SBQ_ENT_N   = 6;                             // SBQ  entry    

//todo
`ifdef CHUNJUN_SUPPORT_V_EXT
localparam LVQID_W     = (LSQID_W > VLQID_W) ? LSQID_W : VLQID_W;
`else   // CHUNJUN_SUPPORT_V_EXT
localparam LVQID_W     = LSQID_W;
`endif  // CHUNJUN_SUPPORT_V_EXT
localparam ID_EXT_W    = (LVQID_W > SBQID_W) ? LVQID_W : SBQID_W;   // Extended ID width stored in missbuffer.
 

localparam MBUS_W      = `CHUNJUN_MBUS_DATA_W;  //64bit

// 011 - 8  bytes per transfer
// 100 - 16 bytes per transfer
localparam MBUS_SIZE_N = $clog2(MBUS_W/8);   

// refill r-channel or eviction w-channel burst length, depend on mbus data width.
localparam LINE_LEN_N  = 256 / MBUS_W;        //refill need four times
localparam LINE_CNT_W  = $clog2(LINE_LEN_N);  //refill count index 

localparam MBUS_WORD_N = MBUS_W/32;           //double include two word
// addr offset width to mux load response data from mbus data.
localparam LD_OFFS_W = $clog2(MBUS_WORD_N);   //The index of word in doubleword

// Burst length = AxLEN +1, i.e., refill/eviction burst length is 4.
// Burst length (LINE_LEN_N) = 256 / MBUS_W.
localparam AXI_LINE_LEN_N = LINE_LEN_N - 1; //brust times
localparam AXI_BURST_INCR = 2'b01;
localparam AXI_BURST_WRAP = 2'b10;

//todo
// ARCACHE[3:0] |   AWCACHE[3:0]    |   Memory type
// 0b0000       |   0b0000          |   Device Non-bufferable
// 0b0001       |   0b0001          |   Device Bufferable
// 0b0010       |   0b0010          |   Normal Non-cacheable Non-bufferable
// 0b0011       |   0b0011          |   Normal Non-cacheable Bufferable
// 0b1010       |   0b0110          |   Write-Through No-Allocate
// 0b1110       |   0b0110          |   Write-Through Read-Allocate
// 0b1010       |   0b1110          |   Write-Through Write-Allocate
// 0b1110       |   0b1110          |   Write-Through Read and Write-Allocate
// 0b1011       |   0b0111          |   Write-Back No-Allocate
// 0b1111       |   0b0111          |   Write-Back Read-Allocate
// 0b1011       |   0b1111          |   Write-Back Write-Allocate
// 0b1111       |   0b1111          |   Write-Back Read and Write-Allocate

localparam AXI_CACHE_DEVICE = 4'b0000;  // Device Bufferable
localparam AXI_CACHE_NC     = 4'b0010;  // Normal Non-cacheable Bufferable

// ARID rules
// 4'b0001:  Exclusive load ID
// 4'b0011:  Device load ID
// 4'b1xxx:  Normal ID

localparam AXI_ID_EXCLD     = 4'b0001; //exc load
localparam AXI_ID_DVCLD     = 4'b0011; //Device Load

// Replay cause encode:
// 4'b0001: Miss Buffer Full
// 4'b0010: All Way Lock
// 4'b0100: Miss Hit Miss
// 4'b1000: Hit Evict Buffer / Auto WakeUp
//
// dcu_sbu_tag_replay_cause_m2 has one more encode:
// 5'b10000: Tag Check ECC Error
 
localparam REPLAY_MBF = 0; //Miss Buffer Full
localparam REPLAY_AWL = 1; //All Way Lock
localparam REPLAY_MHM = 2; //miss hit miss
localparam REPLAY_HEB = 3; //Hit Evict Buffer
localparam REPLAY_AWU = 3; //Auto WakeUp
localparam REPLAY_TCE = 4; //Tag Check ECC Error


// Compare whether id is younger than flush_id.
// - Wrap_bit equals, index (exclude wrap_bit) greater than or equal flush_index.
// - Wrap_bit not equal, index (exclude wrap_bit) less than flush_index.
function id_younger; //1 = younger(need flush), 0 = old(no need flush)
  input logic [LSQID_W-1:0] id;     //id
  input logic [LSQID_W-1:0] cmp_id; //flush_id
  begin
    id_younger = (id[LSQID_W-1] ^ cmp_id[LSQID_W-1]) ?   //Wrap_bit not equal
                    (id[LSQID_W-2:0] <  cmp_id[LSQID_W-2:0]) : //Wrap_bit not equal ->    index less than  flush_index
                    (id[LSQID_W-2:0] >= cmp_id[LSQID_W-2:0]) ; //Wrap_bit equals    ->    index than/equal flush_index
  end
endfunction

// Main arbiter priority:
// CMO/ECC > load > pre-arbiter
localparam ARB_CMO = 0;
localparam ARB_LD  = 1;
localparam ARB_PAB = 2;

// TAG RAM pre-arbiter priority:
// linefill > store data req > store tag check
localparam PAB_TAG_LFL = 0;
localparam PAB_TAG_STD = 1;
localparam PAB_TAG_STC = 2;

// DATA RAM pre-arbiter priority:
// linefill > store data req > evict read cacheline
localparam PAB_DAT_LFL = 0;
localparam PAB_DAT_STD = 1;
localparam PAB_DAT_EVC = 2;

// Pipeline type
localparam TAG_TYPE_LD  = 0;    // load             rd tag_ram
localparam TAG_TYPE_STC = 1;    // store tag check  rd tag_ram
localparam TAG_TYPE_PRF = 2;    // prefetch         rd tag_ram
localparam DAT_TYPE_LD  = 0;    // load             rd data_ram
localparam DAT_TYPE_STD = 1;    // store tag check  rd data_ram
localparam DAT_TYPE_EVC = 2;    // ect              rd data_ram

// Age-matrix select index
localparam AGM_AR  = 0; // Address Ready
localparam AGM_LFL = 1; // Line Fill
localparam AGM_EVC = 2; // Eviction
localparam AGM_AW  = 3; // Address Write
localparam AGM_W   = 4; // data write

localparam MA_WR_ALLOC = 2; // WR_ALLOC mode
localparam MA_RD_ALLOC = 1; // RD_ALLOC mode
localparam MA_CACHE    = 0; // cacheble

localparam DATA_ECC_MODE = 0;   // 0: M2 stage; 1: M3 stage

//**********************************************************************************************//
//**********************************************************************************************//
`ifdef CHUNJUN_SUPPORT_DCACHE
//**********************************************************************************************//
//**********************************************************************************************//


//=================================================================================
// Local Variables 
//=================================================================================

logic [1:0]                         ld_req_valid_m1      ;
logic [1:0][31:0]                   ld_req_addr_m1       ;
logic [1:0][1:0]                    ld_req_size_m1       ; 
logic [1:0][ID_EXT_W-1:0]           ld_req_id_m1         ;
logic [1:0]                         ld_req_idempotency_m1;
logic [1:0][2:0]                    ld_req_inner_ma_m1   ;
logic [1:0][2:0]                    ld_req_outer_ma_m1   ;
logic [1:0]                         ld_req_excl_m1       ;
logic [1:0]                         ld_req_master_m1     ;
logic                               ld_req_samecl_m1     ;
logic [1:0]                         ld_req_cancel_m2     ;
logic                               ld_src_m2;
logic [MB_ENT_N-1:0]                mb_ldsrc ;

logic                               ld0_no_ncdvc;
logic                               ld1_no_ncdvc;
logic [WAY_N-1:0]                   ld_tag_bank_cs ;
logic [SETIDX_W-1:0]                ld_tag_setidx  ;
logic [1:0]                         ldxb_ebank_req;
logic [1:0]                         ldxb_obank_req;
logic [1:0]                         ldxb_ebank_grt;
logic [1:0]                         ldxb_obank_grt;
logic [WAY_N-1:0]                   ld_dat_ebank_cs;
logic [WAY_N-1:0]                   ld_dat_obank_cs;
logic [DAT_RAM_A_W-1:0]             ld_dat_ebank_addr;
logic [DAT_RAM_A_W-1:0]             ld_dat_obank_addr;
logic                               ld1_hit_utag_m1;
logic                               ld1_dual_iss_m1;
logic [WAY_N-1:0]                   utag_ld1_hit_way_m1;
logic [WAY_N-1:0]                   utag_ld1_bank_pair_m1;

logic [WAY_N-1:0]                   sbu_tag_req_bank_cs;
logic [WAY_N-1:0]                   sbu_dcu_data_req_wayoh;
logic [1:0]                         sbu_data_req_addr_offs;
logic [WAY_N-1:0]                   sbu_data_req_bank_pair;
logic                               sbu_data_access_ebank;
logic                               sbu_data_access_obank;
logic [WAY_N-1:0]                   sbu_data_req_ebank_cs;
logic [WAY_N-1:0]                   sbu_data_req_obank_cs;
logic [TAG_FULL_W-1:0]              sbu_data_req_tag_wdata;
logic [DAT_RAM_D_W-1:0]             sbu_data_req_ebank_wen;
logic [DAT_RAM_D_W-1:0]             sbu_data_req_obank_wen;

logic [2:0]                         arb_req;
logic [2:0]                         arb_grt;
logic [2:0]                         pab_tag_req;
logic [2:0]                         pab_tag_grt;
logic [2:0]                         pab_dat_req;
logic [2:0]                         pab_dat_grt;
logic                               ld0_grt;
logic                               ld1_grt;
logic                               stc_grt;
logic                               stc_cmo_grt;
logic                               prefetch_grt;

logic [WAY_N-1:0]                   pab_tag_bank_cs  ;
logic [SETIDX_W-1:0]                pab_tag_setidx   ;
logic                               pab_tag_wr       ;
logic [TAG_FULL_W-1:0]              pab_tag_wdata    ;
logic [TAG_FULL_W-1:0]              tag_wdata_preecc ;
logic [TAG_RAM_D_W-1:0]             tag_wdata_postecc;
 
logic [WAY_N-1:0]                   pab_dat_ebank_cs     ;
logic [WAY_N-1:0]                   pab_dat_obank_cs     ;
logic [SETIDX_W-1:0]                pab_dat_setidx       ;
 
logic [1:0]                         pab_data_bank01_offs ;
logic [1:0]                         pab_data_bank23_offs ;
logic [1:0]                         pab_data_bank45_offs ;
logic [1:0]                         pab_data_bank67_offs ;
logic                               pab_dat_wr           ;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]  pab_dat_ebank_wdata  ;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]  pab_dat_obank_wdata  ;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]  pab_dat_ebank_wen    ;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]  pab_dat_obank_wen    ;
logic                               dcu_ram_data_wr      ;

logic                               dff_tag_type_m2_set;
logic                               dff_tag_type_m2_clr;
logic                               dff_tag_type_m2_en ;
logic [2:0]                         dff_tag_type_m2_d  ;
logic [2:0]                         dff_tag_type_m2_q  ;
logic [2:0]                         tag_info_sel;
logic                               dff_addr_m2_en;
logic [31:0]                        dff_addr_m2_d ;
logic [31:0]                        dff_addr_m2_q ;
logic                               dff_info_m2_en;
logic                               dff_idemp_m2_d ;
logic                               dff_idemp_m2_q ;
logic [2:0]                         dff_ima_m2_d ;
logic [2:0]                         dff_ima_m2_q ;
logic [2:0]                         dff_oma_m2_d ;
logic [2:0]                         dff_oma_m2_q ;
logic [1:0]                         dff_size_m2_d ;
logic [1:0]                         dff_size_m2_q ;
logic                               dff_excl_m2_d ;
logic                               dff_excl_m2_q ;
logic                               dff_priv_m2_d ;
logic                               dff_priv_m2_q ;
logic                               dff_master_m2_d ;
logic                               dff_master_m2_q ;
logic [ID_EXT_W-1:0]                dff_tag_id_m2_d ;
logic [ID_EXT_W-1:0]                dff_tag_id_m2_q ;
logic                               dff_dat_type_m2_en ;
logic [2:0]                         dff_dat_type_m2_d  ;
logic [2:0]                         dff_dat_type_m2_q  ;
logic                               dff_dat_info_m2_en ;
logic [ID_EXT_W-1:0]                dff_dat_id_m2_d ;
logic [ID_EXT_W-1:0]                dff_dat_id_m2_q ;
logic                               dff_dat_rd_m2_d  ;
logic                               dff_dat_rd_m2_q  ;
logic                               dff_samecl_m2_d ;
logic                               dff_samecl_m2_q ;
logic                               dff_ld1_hit_utag_m2_d ;
logic                               dff_ld1_hit_utag_m2_q ;
logic [31:0]                        dff_ld1_addr_m2_d ;
logic [31:0]                        dff_ld1_addr_m2_q ;
logic [WAY_N-1:0]                   dff_dat_way_m2_d ;
logic [WAY_N-1:0]                   dff_dat_way_m2_q ;
logic                               flush_ppln_ld0_m2;
logic                               flush_ppln_ld1_m2;
logic                               ld0_real_vld_m2;
logic                               ld1_real_vld_m2;
logic                               real_idemp_m2;
logic [2:0]                         real_ima_m2;
logic [2:0]                         real_oma_m2;

logic                               dff_std_vld_m3_set;
logic                               dff_std_vld_m3_clr;
logic                               dff_std_vld_m3_en ;
logic                               dff_std_vld_m3_d  ;
logic                               dff_std_vld_m3_q  ;
logic                               dff_dat_id_m3_en;
logic [ID_EXT_W-1:0]                dff_dat_id_m3_d ;
logic [ID_EXT_W-1:0]                dff_dat_id_m3_q ;
logic                               dff_data_m3_en;
logic [31:0]                        dff_ebank_data_m3_d;
logic [31:0]                        dff_ebank_data_m3_q;
logic [31:0]                        dff_obank_data_m3_d;
logic [31:0]                        dff_obank_data_m3_q;

logic                               dff_tag_ram_read_m2_en ;
logic [WAY_N-1:0]                   tag_ram_read_m1        ;
logic [WAY_N-1:0]                   dff_tag_ram_read_m2_d  ;
logic [WAY_N-1:0]                   dff_tag_ram_read_m2_q  ;
logic [WAY_N-1:0]                   tag_rdata_valid_bit;
logic [WAY_N-1:0]                   tag_rdata_dirty_bit;
logic [WAY_N-1:0][TAG_W-1:0]        tag_rdata_tag_bits;
logic [WAY_N-1:0]                   tag_way_cmp;
logic                               tag_raw_hit ;
logic                               tag_raw_miss;
logic                               ld0_tag_is_hit;
logic                               stc_tag_is_hit;
logic                               tag_is_miss ;
logic                               tag_hit_dirty_bit;
 
logic [1:0]                         dat_ebank_dword_offs;
logic [1:0]                         dat_obank_dword_offs;
logic [WAY_N-1:0]                   hg_dat_ebank_sel;
logic [WAY_N-1:0]                   hg_dat_obank_sel;
logic                               data_ebank_pick_nohg;
logic                               data_obank_pick_nohg;
logic [WAY_N-1:0]                   dat_ebank_sel;
logic [WAY_N-1:0]                   dat_obank_sel;
logic [DAT_RAM_D_W-1:0]             full_data_ebank_m2;
logic [DAT_RAM_D_W-1:0]             full_data_obank_m2;
 
logic                               no_ncdvc_m2;
logic                               cache_alloc_m2;
logic                               hit_update_plru;
logic                               need_refill;
logic                               plru_valid;
logic [WAY_N-1:0]                   plru_victim_way;
logic                               victim_way_valid_bit;
logic                               victim_way_dirty_bit;
logic [TAG_W-1:0]                   victim_way_tag_bits;
logic                               tag_way_all_vld;
logic [WAY_N-1:0]                   tag_way_invld;
logic [WAY_N-1:0]                   invld_victim_way;
logic [WAY_N-1:0]                   victim_way;
 
logic [SBQ_ENT_N-1:0]               sbu_ent_eq_setidx_m2;
logic [SBQ_ENT_N-1:0]               sbu_ent_lock_vld    ;
logic [SBQ_ENT_N-1:0][WAY_N-1:0]    sbu_ent_lock_way    ;
logic [WAY_N-1:0]                   sbu_locked_way;
logic [WAY_N-1:0]                   mb_locked_way ;
logic [WAY_N-1:0]                   locked_way_m2 ;
logic                               all_way_locked;

logic                                       mb_push_en;
logic [MB_ID_W-1:0]                         mb_push_idx;
logic [MB_ENT_N-1:0]                        mb_push_bitmap;
logic                                       mb_full   ;
logic                                       mb_empty  ;
logic [MB_ENT_N-1:0]                        mb_pop_lfl     ;
logic [MB_ENT_N-1:0]                        mb_pop_no_lfl  ;
logic [MB_ENT_N-1:0]                        mb_pop_cbom    ;

logic [MB_ENT_N-1:0]                        dff_mb_vld_set ;
logic [MB_ENT_N-1:0]                        dff_mb_vld_clr ;
logic [MB_ENT_N-1:0]                        dff_mb_vld_en  ;
logic [MB_ENT_N-1:0]                        dff_mb_vld_d   ;
logic [MB_ENT_N-1:0]                        dff_mb_vld_q   ;
logic [MB_ENT_N-1:0]                        dff_mb_flush_set;
logic [MB_ENT_N-1:0]                        dff_mb_flush_clr;
logic [MB_ENT_N-1:0]                        dff_mb_flush_en ;
logic [MB_ENT_N-1:0]                        dff_mb_flush_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_flush_q  ;
logic [MB_ENT_N-1:0]                        dff_mb_info_en      ;
logic               [32-1:0]     dff_mb_addr_d       ;
logic [MB_ENT_N-1:0][32-1:0]     dff_mb_addr_q       ;
logic               [1:0]                   dff_mb_size_d       ;
logic [MB_ENT_N-1:0][1:0]                   dff_mb_size_q       ;
logic                                       dff_mb_is_refill_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_is_refill_q  ;
logic                                       dff_mb_idemp_d      ;
logic [MB_ENT_N-1:0]                        dff_mb_idemp_q      ;
logic               [2:0]                   dff_mb_oma_d        ;
logic [MB_ENT_N-1:0][2:0]                   dff_mb_oma_q        ;
logic                                       dff_mb_excl_d       ;
logic [MB_ENT_N-1:0]                        dff_mb_excl_q       ;
logic                                       dff_mb_priv_d       ;
logic [MB_ENT_N-1:0]                        dff_mb_priv_q       ;
logic                                       dff_mb_master_d     ;
logic [MB_ENT_N-1:0]                        dff_mb_master_q     ;
logic               [ID_EXT_W-1:0]          dff_mb_extid_d      ;
logic [MB_ENT_N-1:0][ID_EXT_W-1:0]          dff_mb_extid_q      ;
logic               [WAY_N-1:0]             dff_mb_victim_way_d ;
logic [MB_ENT_N-1:0][WAY_N-1:0]             dff_mb_victim_way_q ;
logic [2:0]                                 dff_mb_tag_type_d   ;
logic [MB_ENT_N-1:0][2:0]                   dff_mb_tag_type_q   ;
logic [MB_ENT_N-1:0]                        dff_mb_ar_sent_set;
logic [MB_ENT_N-1:0]                        dff_mb_ar_sent_clr;
logic [MB_ENT_N-1:0]                        dff_mb_ar_sent_en ;
logic [MB_ENT_N-1:0]                        dff_mb_ar_sent_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_ar_sent_q  ;
logic [MB_ENT_N-1:0]                        dff_mb_r_cnt_en  ;
logic [MB_ENT_N-1:0][LINE_CNT_W-1:0]        dff_mb_r_cnt_d   ;
logic [MB_ENT_N-1:0][LINE_CNT_W-1:0]        dff_mb_r_cnt_q   ;
logic [MB_ENT_N-1:0][LINE_CNT_W-1:0]        refill_rot_offs  ;
logic [MB_ENT_N-1:0]                        dff_mb_r_done_set;
logic [MB_ENT_N-1:0]                        dff_mb_r_done_clr;
logic [MB_ENT_N-1:0]                        dff_mb_r_done_en ;
logic [MB_ENT_N-1:0]                        dff_mb_r_done_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_r_done_q  ;
logic [MB_ENT_N-1:0]                        dff_mb_r_err_set;
logic [MB_ENT_N-1:0]                        dff_mb_r_err_clr;
logic [MB_ENT_N-1:0]                        dff_mb_r_err_en ;
logic [MB_ENT_N-1:0]                        dff_mb_r_err_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_r_err_q  ;

logic [MB_ENT_N-1:0]                        bmu_dcu_r_sel;
logic [MB_ENT_N-1:0]                        mb_ent_r_bitmap;
logic [MB_ENT_N-1:0][WAY_N-1:0]             dff_mb_data_en;
logic [MB_ENT_N-1:0][255:0]                 dff_mb_data_d ;
logic [MB_ENT_N-1:0][255:0]                 dff_mb_data_q ;
logic [MB_ENT_N-1:0][WAY_N-1:0]             refill_wr_data_en;
logic [255:0]                               refill_line_data;
logic [WAY_N-1:0]                           rfl_victim_way;
logic [MB_ENT_N-1:0][LD_OFFS_W-1:0]         mb_addr_ldoffs;
logic [MB_ENT_N-1:0]                        hit_mb_ent_m2;
logic                                       hit_mb_m2;
logic [MB_ENT_N-1:0]                        mb_ent_eq_setidx_m2;
logic [MB_ENT_N-1:0]                        mb_ent_lock_vld;
logic                                       dff_mb_rls_array_en;
logic [MB_ENT_N-1:0]                        dff_mb_rls_array_d ;
logic [MB_ENT_N-1:0]                        dff_mb_rls_array_q ;

logic [4:0][MB_ENT_N-1:0]                   agm_query_vld_mask;
logic [4:0][MB_ENT_N-1:0]                   agm_oldest_oh;

logic                                       eb_push_en ;
logic [MB_ENT_N-1:0]                        dff_eb_vld_set;
logic [MB_ENT_N-1:0]                        dff_eb_vld_clr;
logic [MB_ENT_N-1:0]                        dff_eb_vld_en ;
logic [MB_ENT_N-1:0]                        dff_eb_vld_d  ;
logic [MB_ENT_N-1:0]                        dff_eb_vld_q  ;
logic [MB_ENT_N-1:0]                        dff_eb_tag_en;
logic               [TAG_W-1:0]             dff_eb_tag_d ;
logic [MB_ENT_N-1:0][TAG_W-1:0]             dff_eb_tag_q ;
logic [MB_ENT_N-1:0]                        dff_eb_priv_en;
logic                                       dff_eb_priv_d;
logic [MB_ENT_N-1:0]                        dff_eb_priv_q;
logic [MB_ENT_N-1:0][SETIDX_W-1:0]          eb_setidx ;
logic [MB_ENT_N-1:0]                        dff_eb_evc_sent_set;
logic [MB_ENT_N-1:0]                        dff_eb_evc_sent_clr;
logic [MB_ENT_N-1:0]                        dff_eb_evc_sent_en;
logic [MB_ENT_N-1:0]                        dff_eb_evc_sent_d ;
logic [MB_ENT_N-1:0]                        dff_eb_evc_sent_q ;
logic [MB_ENT_N-1:0]                        dff_eb_data_rdy_set;
logic [MB_ENT_N-1:0]                        dff_eb_data_rdy_clr;
logic [MB_ENT_N-1:0]                        dff_eb_data_rdy_en;
logic [MB_ENT_N-1:0]                        dff_eb_data_rdy_d ;
logic [MB_ENT_N-1:0]                        dff_eb_data_rdy_q ;
logic [MB_ENT_N-1:0]                        dff_eb_aw_sent_set;
logic [MB_ENT_N-1:0]                        dff_eb_aw_sent_clr;
logic [MB_ENT_N-1:0]                        dff_eb_aw_sent_en ;
logic [MB_ENT_N-1:0]                        dff_eb_aw_sent_d  ;
logic [MB_ENT_N-1:0]                        dff_eb_aw_sent_q  ;
logic [MB_ENT_N-1:0]                        dff_eb_w_sent_set ;
logic [MB_ENT_N-1:0]                        dff_eb_w_sent_clr ;
logic [MB_ENT_N-1:0]                        dff_eb_w_sent_en  ;
logic [MB_ENT_N-1:0]                        dff_eb_w_sent_d   ;
logic [MB_ENT_N-1:0]                        dff_eb_w_sent_q   ;
logic [MB_ENT_N-1:0]                        dff_eb_done_set;
logic [MB_ENT_N-1:0]                        dff_eb_done_clr;
logic [MB_ENT_N-1:0]                        dff_eb_done_en;
logic [MB_ENT_N-1:0]                        dff_eb_done_d ;
logic [MB_ENT_N-1:0]                        dff_eb_done_q ;
logic [MB_ENT_N-1:0]                        bmu_dcu_b_sel;
logic [MB_ENT_N-1:0]                        eb_ent_b_bitmap;
logic [MB_ENT_N-1:0]                        hit_eb_ent_m2;
logic [MB_ENT_N-1:0]                        hit_eb_ent_ndone_m2;
logic [MB_ENT_N-1:0]                        hit_eb_ent_done_m2 ;
logic                                       hit_eb_as_replay_m2;
logic                                       hit_eb_m2;
logic                                       hit_eb_as_miss_m2  ;
logic [MB_ENT_N-1:0]                        ld1_hit_eb_ent_done_m2;
logic                                       ld1_hit_eb_done_m2 ;
logic [MB_ENT_N-1:0]                        replay_hit_mb_ent  ;
logic [MB_ID_W-1:0 ]                        replay_hit_mb_id   ;
logic                                       dff_eb_rls_array_en;
logic [MB_ENT_N-1:0]                        dff_eb_rls_array_d ;
logic [MB_ENT_N-1:0]                        dff_eb_rls_array_q ;
logic                                       eb_b_err;
logic [31:0]                                eb_b_addr;
 
logic                                       evc_rd_vld;
logic                                       evc_rd_rdy;
logic [SETIDX_W-1:0]                        evc_setidx;
logic [WAY_N-1:0]                           evc_way;
logic [MB_ID_W-1:0]                         evc_id ;
logic [255:0]                               evc_line_raw_data;
logic [MB_ENT_N-1:0]                        evc_wr_sel;
logic [MB_ENT_N-1:0]                        evc_wr_mb_bitmap;
logic [MB_ENT_N-1:0][31:0]                  eb_addr;
logic [WAY_N-1:0]                           line_data_way;
logic [1:0]                                 line_data_addr01 ;
logic [1:0]                                 line_data_addr23 ;
logic [1:0]                                 line_data_addr45 ;
logic [1:0]                                 line_data_addr67 ;
logic                                       lfl_wr_vld;
logic                                       lfl_wr_rdy;
logic [WAY_N-1:0]                           lfl_way ;
logic [WAY_N-1:0]                           lfl_tag_bank_cs;
logic [WAY_N-1:0]                           lfl_dat_bank_cs;
logic [31:0]                                lfl_addr;
logic                                       lfl_err ;
logic [255:0]                               lfl_data;
logic [2:0]                                 lfl_tag_type;
logic [ID_EXT_W-1:0]                        lfl_extid;
logic [TAG_FULL_W-1:0]                      lfl_tag_wdata;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]          lfl_dat_ebank_wdata;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]          lfl_dat_obank_wdata;
logic [DAT_RAM_D_W-1:0]                     sbu_dcu_ebank_wdata;
logic [DAT_RAM_D_W-1:0]                     sbu_dcu_obank_wdata;
 
 logic [MB_ID_W-1:0]                         mb_ar_mux_idx       ;
logic [32-1:0]                   mb_ar_mux_addr      ;
logic [1:0]                                 mb_ar_mux_size      ;
logic [2:0]                                 mb_ar_mux_oma       ;
logic                                       mb_ar_mux_excl      ;
logic                                       mb_ar_mux_priv      ;
logic                                       mb_ar_mux_master    ;
logic                                       mb_ar_mux_is_refill ;
logic                                       mb_ar_mux_idemp     ;
logic [MBUS_SIZE_N-1:0]                     dcu_bmu_ar_addr_offs;
logic                                       dcu_bmu_ar_hdsk     ;
logic                                       bmu_dcu_r_hdsk      ;
logic                                       dff_remap_id_en     ;
logic [MB_ID_W-1:0]                         dff_remap_id_d      ;
logic [MB_ID_W-1:0]                         dff_remap_id_q      ;

logic                                       dcu_bmu_aw_hdsk;
logic                                       dcu_bmu_w_hdsk ;
logic                                       bmu_dcu_b_hdsk ;
logic [MB_ID_W-1:0]                         eb_aw_mux_idx  ;
logic [32-1:0]                   eb_aw_mux_addr ;
logic                                       eb_aw_mux_master;
logic                                       eb_aw_mux_priv;
logic                                       dff_eb_w_cnt_en;
logic [LINE_CNT_W-1:0]                      dff_eb_w_cnt_d;
logic [LINE_CNT_W-1:0]                      dff_eb_w_cnt_q;
logic [WAY_N-1:0]                           eb_w_mux_way  ;
logic [255:0]                               eb_w_mux_data ;
logic [31:0]                                eb_w_mux_addr ;
logic [WAY_N-1:0]                           eb_wdata_sel  ;
logic [MBUS_W-1:0]                          eb_wdata      ;

logic [LD_OFFS_W-1:0]                       bmu_dcu_r_ldoffs;
logic [2:0]                                 bmu_dcu_r_tagtype;
logic                                       bmu_dcu_r_flushed;
logic                                       bmu_dcu_r_ldsrc  ;
logic [LINE_CNT_W-1:0]                      bmu_dcu_r_cnt;
logic [ID_EXT_W-1:0]                        bmu_dcu_r_extid;
logic                                       r_is_critword;
logic                                       bmu_r_ld0_critword ;
logic                                       bmu_r_ld0_ncritword;
logic                                       bmu_r_stc          ;
logic                                       bmu_r_prefetch     ;
logic [31:0]                                bmu_r_ld0_data;
logic                                       ld0_hit_m2;
logic [31:0]                                ld0_hit_data_m2;
logic [ID_EXT_W-1:0]                        ld0_hit_id_m2 ;
logic                                       ld1_hit_m2;
logic [31:0]                                ld1_hit_data_m2;
logic [ID_EXT_W-1:0]                        ld1_hit_id_m2 ;
logic                                       ld0_rsp_grt_hit;
logic                                       ld0_rsp_grt_bmu;
logic [3:0]                                 ld0_replay_cause_m2;
logic [3:0]                                 ld1_replay_cause_m2;
logic                                       ld_vld_m3;
logic [MB_ID_W-1:0]                         ld_r_id_remap;

logic [WAY_N-1:0]                           dcu_sbu_tag_rsp_way_oh;
 
logic                                       dff_init_done_en ;
logic                                       dff_init_done_d  ;
logic                                       dff_init_done_q  ;
logic                                       invld_raw_req    ;
logic                                       invld_arb_req    ;
logic                                       dff_invld_cnt_en ;
logic [SETIDX_W-1:0]                        dff_invld_cnt_d  ;
logic [SETIDX_W-1:0]                        dff_invld_cnt_q  ;
logic                                       dff_invld_cnt_offs_en;
logic [1:0]                                 dff_invld_cnt_offs_d ;
logic [1:0]                                 dff_invld_cnt_offs_q ;
logic                                       cnt_invld_reached ;
logic                                       cmo_raw_req  ;
logic                                       cmo_arb_req  ;
logic [WAY_N-1:0]                           cmo_tag_bank_cs;
logic                                       cmo_tag_wr    ;
logic [SETIDX_W-1:0]                        cmo_tag_setidx;
logic [TAG_FULL_W-1:0]                      cmo_tag_wdata ;
logic [WAY_N-1:0]                           cmo_dat_ebank_cs;
logic [WAY_N-1:0]                           cmo_dat_obank_cs;
logic                                       cmo_dat_wr    ;
logic [DAT_RAM_A_W-1:0]                     cmo_dat_ebank_addr;
logic [DAT_RAM_A_W-1:0]                     cmo_dat_obank_addr;
logic [DAT_RAM_D_W-1:0]                     cmo_dat_ebank_wdata ;
logic [DAT_RAM_D_W-1:0]                     cmo_dat_obank_wdata ;
logic [DAT_RAM_D_W-1:0]                     cmo_dat_wen   ;
logic                                       cmo_idle      ;
logic                                       pipeline_idle ;
 
logic                                       sbu_dcu_cbom_req;
logic                                       cbom_raw_req    ;
logic                                       cbom_arb_req    ;
logic                                       cbom_state_idle ;
logic                                       cbom_state_rdtag;
logic                                       cbom_state_into_eb;
logic                                       cbom_state_chk    ;
logic                                       cbom_state_clean  ;
logic                                       cbom_state_inval  ;
logic [4:0]                                 dff_cbom_state_d;
logic [4:0]                                 dff_cbom_state_q;
 
logic                                       dff_cbom_addr_en;
logic [31:0]                                dff_cbom_addr_d ;
logic [31:0]                                dff_cbom_addr_q ;
logic                                       dff_cbom_master_en;
logic                                       dff_cbom_master_d ;
logic                                       dff_cbom_master_q ;
logic                                       dff_cbom_op_en;
logic [1:0]                                 dff_cbom_op_d ;
logic [1:0]                                 dff_cbom_op_q ;
logic                                       dff_cbom_way_en;
logic [WAY_N-1:0]                           dff_cbom_way_d ;
logic [WAY_N-1:0]                           dff_cbom_way_q ;
logic                                       dff_cbom_tag_en;
logic [TAG_W-1:0]                           dff_cbom_tag_d ;
logic [TAG_W-1:0]                           dff_cbom_tag_q ;
logic                                       dff_cbom_clean_err_set;
logic                                       dff_cbom_clean_err_clr;
logic                                       dff_cbom_clean_err_en ;
logic                                       dff_cbom_clean_err_d  ;
logic                                       dff_cbom_clean_err_q  ;
logic [31:0]                                cbom_addr_m1  ;
logic                                       cbom_op_inval ;
logic                                       cbom_op_clean ;
logic                                       cbom_op_flush ;

logic                                       sbu_dcu_calm_req;
logic                                       sbu_dcu_calm_clean;
logic [1:0]                                 dff_calm_state_d;
logic [1:0]                                 dff_calm_state_q;
logic                                       calm_state_idle;
logic                                       calm_state_busy;
logic                                       calm_state_clean;
logic                                       calm_state_inval;
logic                                       dff_calm_cl_cnt_en ;
logic [SETIDX_W-1:0]                        dff_calm_cl_cnt_d  ;
logic [SETIDX_W-1:0]                        dff_calm_cl_cnt_q  ;
logic                                       dff_calm_way_cnt_en;
logic [1:0]                                 dff_calm_way_cnt_d ;
logic [1:0]                                 dff_calm_way_cnt_q ;
logic [3:0]                                 calm_clean_way     ;
logic                                       calm_clean_incr    ;
logic                                       calm_clean_reached ;
logic                                       calm_mux_tag_vld   ;
logic                                       calm_mux_tag_dirty ;
logic                                       calm_mux_tag_vld_dirty;

logic [WAY_N-1:0]                           cbom_tag_bank_cs;
logic                                       cbom_tag_wr    ;
logic [SETIDX_W-1:0]                        cbom_tag_setidx;
logic [TAG_FULL_W-1:0]                      cbom_tag_wdata ;

logic [WAY_N-1:0]                           ram_tag_ecc_sec_m2;
logic [WAY_N-1:0]                           ram_tag_ecc_ded_m2;
logic                                       tag_ecc_bang_m2;
logic                                       tag_ecc_sec_m2 ;
logic                                       tag_ecc_ded_m2 ;
logic                                       ebank_ecc_ded_m3;
logic                                       obank_ecc_ded_m3;
logic                                       ebank_ecc_sec_m3;
logic                                       obank_ecc_sec_m3;
logic [WAY_N-1:0]                           ebank_ecc_pair_m3;
logic [WAY_N-1:0]                           obank_ecc_pair_m3;
logic                                       data_ecc_sec_m3;
logic                                       data_ecc_ded_m3;
logic                                       data_ecc_err_m3;
logic                                       eb_ecc_err     ;
logic                                       eb_ecc_sec     ;
logic                                       eb_ecc_ded     ;
logic                                       ecc_arb_req    ;
logic [WAY_N-1:0]                           ecc_tag_bank_cs;
logic [SETIDX_W-1:0]                        ecc_tag_setidx ;
logic [WAY_N-1:0][TAG_RAM_D_W-1:0]          ecc_tag_wdata  ;
logic [WAY_N-1:0]                           ecc_ebank_cs;
logic [WAY_N-1:0]                           ecc_obank_cs;
logic [31:0]                                ecc_ebank_addr;
logic [31:0]                                ecc_obank_addr;
logic [DAT_RAM_D_W-1:0]                     ebank_data_m3;
logic [DAT_RAM_D_W-1:0]                     obank_data_m3;
 
logic [WAY_N-1:0][TAG_RAM_D_W-1:0]          ram_tag_rdata;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]          ram_data0_rdata;
logic [WAY_N-1:0][DAT_RAM_D_W-1:0]          ram_data1_rdata;

logic                                       csr_dcache_en  ;
logic                                       csr_ecc_en     ;
logic                                       csr_inject_en  ;
logic [3:0]                                 csr_inject_dest;
logic [3:0]                                 csr_inject_bank;
logic [31:0]                                csr_inject_bitmapl;
logic [31:0]                                csr_inject_bitmaph;

//=================================================================================
// VPU and LSU request mux
//=================================================================================

// VPU and LSU requests are mutually exclusive.
// This constraint could simplify implementation. 
// OR these interface as one unified interface, 
// just distinguish VPU and LSU response at output interface.

`ifdef CHUNJUN_SUPPORT_V_EXT
 
 
logic                                       dff_ld_src_m2_en;
logic                                       dff_ld_src_m2_d ;
logic                                       dff_ld_src_m2_q ;
logic                                       dff_mb_ldsrc_d  ;
logic [MB_ENT_N-1:0]                        dff_mb_ldsrc_q  ;

for (genvar i=0; i<2; i++) begin : g_ld_req

  assign ld_req_valid_m1      [i] = lsu_dcu_ld_req_valid_m1 [i] | vpu_dcu_ld_req_vld_m1[i]; //If either LSU or VPU has a valid request, it is considered that the channel has a valid request
  assign ld_req_cancel_m2     [i] =  ld_src_m2 & lsu_dcu_ld_req_cancel_m2[i]  // If ld_src_m2 is high, use the cancellation signal of LSU
                                  | ~ld_src_m2 & vpu_dcu_ld_req_cancel_m2[i]; // If ld_src_m2 is low, use the cancellation signal of VPU
  //If the VPU has a valid request, use the parameters of the VPU, Otherwise use the parameters of the LSU
  assign ld_req_addr_m1       [i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_addr_m1       [i] : lsu_dcu_ld_req_addr_m1       [i];
  assign ld_req_size_m1       [i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_size_m1       [i] : lsu_dcu_ld_req_size_m1       [i];
  assign ld_req_idempotency_m1[i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_idempotency_m1[i] : lsu_dcu_ld_req_idempotency_m1[i];
  assign ld_req_inner_ma_m1   [i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_inner_ma_m1   [i] : lsu_dcu_ld_req_inner_ma_m1   [i];
  assign ld_req_outer_ma_m1   [i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_outer_ma_m1   [i] : lsu_dcu_ld_req_outer_ma_m1   [i];
  assign ld_req_excl_m1       [i] = vpu_dcu_ld_req_vld_m1[i] ? 1'b0                             : lsu_dcu_ld_req_excl_m1       [i];
  assign ld_req_master_m1     [i] = vpu_dcu_ld_req_vld_m1[i] ? vpu_dcu_ld_req_master_m1     [i] : lsu_dcu_ld_req_master_m1     [i];
  assign ld_req_id_m1         [i] = vpu_dcu_ld_req_vld_m1[i] ? { {ID_EXT_W-VLQID_W{1'b0}}, vpu_dcu_ld_req_vlqid_m1[i] }
                                                             : { {ID_EXT_W-LSQID_W{1'b0}}, lsu_dcu_ld_req_lsqid_m1[i] };
end
 
//vpu0 req, vpu1 noreq, lsu_dcu_ld_req_samecl_m1 must is 0? todo
assign ld_req_samecl_m1 = &vpu_dcu_ld_req_vld_m1 ? vpu_dcu_ld_req_samecl_m1 : lsu_dcu_ld_req_samecl_m1; //When enabling VPN, use "vpn_samecl",


assign dff_ld_src_m2_en = lsu_dcu_ld_req_valid_m1[0] & dcu_lsu_ld_req_ready_m1[0] | //ld request from VPU or LSU
                          vpu_dcu_ld_req_vld_m1  [0] & dcu_vpu_ld_req_rdy_m1  [0] ;
assign dff_ld_src_m2_d  = lsu_dcu_ld_req_valid_m1[0];

`WDFFER(dff_ld_src_m2_q, dff_ld_src_m2_d, dff_ld_src_m2_en, clk, rst_n)

// 0: ld request from VPU
// 1: ld request from LSU
// Record ld from LSU/VPU at M2 stage, to distinguish flush from LSU or VPU.
// Because LSU forward flush to DCU once LSU get flush from PCU.
// todo: delete nouse signal
assign ld_src_m2 = dff_ld_src_m2_q; //0: ld request from VPU, 1: ld request from LSU
 
assign dff_mb_ldsrc_d = ld_src_m2;

for (genvar i=0; i<MB_ENT_N; i++) begin : g_mb_ldsrc
 
  `WDFFER(dff_mb_ldsrc_q[i], dff_mb_ldsrc_d, dff_mb_info_en[i], clk, rst_n) //when need push miss_buf, ldsrc to miss_buf
 
end // g_mb_ldsrc


// todo: delete nouse signal
assign mb_ldsrc = dff_mb_ldsrc_q;  //load type

assign dcu_lsu_ld_req_ready_m1[0] = ld0_grt & lsu_dcu_ld_req_valid_m1[0]; //Arbitration successful and req_valid to LSU
assign dcu_lsu_ld_req_ready_m1[1] = ld1_grt & lsu_dcu_ld_req_valid_m1[1]; //Arbitration successful and req_valid to LSU
 
 // vpu_ld doesn't use ld0_grt / ld1_grt to tune timing.
assign dcu_vpu_ld_req_rdy_m1[0]   = ~cmo_raw_req & ~cmo_arb_req & vpu_dcu_ld_req_vld_m1[0]; //no cmo_req(invld(init.inval_all),ecc,cmo) && no com_arb(no flush) && vpu_vld
assign dcu_vpu_ld_req_rdy_m1[1]   = ~cmo_raw_req & ~cmo_arb_req & (&vpu_dcu_ld_req_vld_m1) & 
                                    vpu_dcu_ld_req_samecl_m1 & csr_dcache_en & //same cacheline && dcache_en
                                    (ldxb_ebank_grt[1] | ldxb_obank_grt[1]) ;  //ld req(bank_grt && select odd/even && nc/devi)
 

`else   // CHUNJUN_SUPPORT_V_EXT


for (genvar i=0; i<2; i++) begin : g_ld_req
 
  assign ld_req_valid_m1      [i] = lsu_dcu_ld_req_valid_m1 [i];
  assign ld_req_cancel_m2     [i] = lsu_dcu_ld_req_cancel_m2[i];

  assign ld_req_addr_m1       [i] = lsu_dcu_ld_req_addr_m1       [i];
  assign ld_req_size_m1       [i] = lsu_dcu_ld_req_size_m1       [i];
  assign ld_req_idempotency_m1[i] = lsu_dcu_ld_req_idempotency_m1[i];
  assign ld_req_inner_ma_m1   [i] = lsu_dcu_ld_req_inner_ma_m1   [i];
  assign ld_req_outer_ma_m1   [i] = lsu_dcu_ld_req_outer_ma_m1   [i];
  assign ld_req_excl_m1       [i] = lsu_dcu_ld_req_excl_m1       [i];
  assign ld_req_master_m1     [i] = lsu_dcu_ld_req_master_m1     [i];
  assign ld_req_id_m1         [i] = { {ID_EXT_W-VLQID_W{1'b0}}, lsu_dcu_ld_req_lsqid_m1[i] };
end

assign ld_req_samecl_m1 = lsu_dcu_ld_req_samecl_m1;

// 0: ld request from VPU
// 1: ld request from LSU
assign ld_src_m2 = 1'b1;  //only ld type, novpn

assign mb_ldsrc = {MB_ENT_N{1'b1}}; //all requests from Missbuf are loads, not VPNs


assign dcu_lsu_ld_req_ready_m1[0] = ld0_grt;

// When load dual-issue is possible, either same-cacheline or micro-tag hit, load1 can be accepted.
assign dcu_lsu_ld_req_ready_m1[1] = ld1_grt;


`endif  // CHUNJUN_SUPPORT_V_EXT

//=================================================================================================
// Load Xbar (load tag_bank cs and load tag_bank addr) (load data_bank cs and load data_bank addr)
//=================================================================================================

// Non-cacheable and device type load request does not access cache RAMs.
assign ld0_no_ncdvc = (ld_req_idempotency_m1[0] & ld_req_inner_ma_m1[0][MA_CACHE]); //ld0 Only when nodevice && cacheble, is it considered a cacheable request.
assign ld1_no_ncdvc = (ld_req_idempotency_m1[1] & ld_req_inner_ma_m1[1][MA_CACHE]); //ld1 Only when nodevice && cacheble, is it considered a cacheable request.

// Except true valid, NC/device load request does not access cache RAMs either.
assign ld_tag_bank_cs = {WAY_N{ld_req_valid_m1[0] & ld0_no_ncdvc}};

assign ld_tag_setidx  = ld_req_addr_m1[0][5+:SETIDX_W]; //tag_ram index is addr[13:5], set=512

// Determine the Data RAM cs(enables).
// - For pre-arbitrated requests, the cs will be calculated in 'Arbiter' setction. And one address select even/odd banks.
// - For load requests, two different addresses select even/odd banks, depend on the load address bit[2].

assign ldxb_ebank_req[0] = ld_req_valid_m1[0] & ~ld_req_addr_m1[0][2] & ld0_no_ncdvc; //ld0 bankreq =  vld && select even addr && nodvc
assign ldxb_ebank_req[1] = ld_req_valid_m1[1] & ~ld_req_addr_m1[1][2] & ld1_no_ncdvc; //ld1 bankreq =  vld && select even addr && nodvc

assign ldxb_obank_req[0] = ld_req_valid_m1[0] &  ld_req_addr_m1[0][2] & ld0_no_ncdvc; //ld0 bankreq =  vld && select odd addr && nodvc
assign ldxb_obank_req[1] = ld_req_valid_m1[1] &  ld_req_addr_m1[1][2] & ld1_no_ncdvc; //ld1 bankreq =  vld && select odd addr && nodvc

// Fixed priority: load0 always higher priority than load1 when bank conflict.

assign ldxb_ebank_grt[0] = ldxb_ebank_req[0];                        //ebank0 higher priority
assign ldxb_ebank_grt[1] = ldxb_ebank_req[1] & ~ldxb_ebank_req[0];   //ebank1 grt when bank0 nogrt

assign ldxb_obank_grt[0] = ldxb_obank_req[0];                       //obank0 higher priority
assign ldxb_obank_grt[1] = ldxb_obank_req[1] & ~ldxb_obank_req[0];  //obank0 higher priority

// Load access four-ways with same address offset in even banks or odd banks, depend on the load address bit[2].
// Load channel 0 is always accepted.
// Load channel 1 is accepted when dual-issue is possible, either same-cacheline or micro-tag hit.

assign ld1_hit_utag_m1 = |utag_ld1_hit_way_m1;                                  //utag只要有一个 way 命中，这个信号就为1。
assign ld1_dual_iss_m1 = (ld_req_samecl_m1 | ld1_hit_utag_m1) & csr_dcache_en;  //(当前load和上一个load在同一个cacheline | load1命中了micro-tag（utag）) & dcache使能时 

// Here don't use (ldxb_ebank_req[1] & ld1_dual_iss_m1) as data bank cs to optimize interface timing,
// at the cost of power.
assign ld_dat_ebank_cs = {WAY_N{ldxb_ebank_req[0] | ldxb_ebank_req[1]}};  //ebank valid when ld0_ebank_req or ld1_ebank_req
assign ld_dat_obank_cs = {WAY_N{ldxb_obank_req[0] | ldxb_obank_req[1]}};  //obank valid when ld0_obank_req or ld1_obank_req

// Determine the Data RAM address.
// For load requests, two different addresses select even/odd banks, depend on the load address bit[2].
// addr[1:0]：字节偏移（byte offset），用于选择32位字内的具体字节
// addr[2]：奇偶bank选择（word select），用于选择odd bank, 还是even bank
// addr[4:3]：bank pair索引，用于选择哪个bank pair
// addr[5:13]：缓存组索引
assign ld_dat_ebank_addr = {DAT_RAM_A_W{ldxb_ebank_grt[0]}} & ld_req_addr_m1[0][3 +: DAT_RAM_A_W]  //ebank 根据ldxb_ebank_grt[0/1]信号选择通道0或通道1的地址
                         | {DAT_RAM_A_W{ldxb_ebank_grt[1]}} & ld_req_addr_m1[1][3 +: DAT_RAM_A_W]; //

assign ld_dat_obank_addr = {DAT_RAM_A_W{ldxb_obank_grt[0]}} & ld_req_addr_m1[0][3 +: DAT_RAM_A_W]  //obank 根据ldxb_obank_grt[0/1]信号选择通道0或通道1的地址
                         | {DAT_RAM_A_W{ldxb_obank_grt[1]}} & ld_req_addr_m1[1][3 +: DAT_RAM_A_W];

//=================================================================================
// SBU Pre-Process, (store tag_bank wdata) (store data_bank cs and store data_bank wdata)
//=================================================================================

// Mask SBU tag check request with non-cmo-op, since we need to grant this SBU tag check request and handshake with SBU.
// But cmo-op does not access tag RAM in the same cycle (through main arbiter later).
assign sbu_tag_req_bank_cs = {WAY_N{1'b1}}; //为所有缓存路（WAY_N个）设置片选信号为高

// Convert way index to way onehot vector.
wing_cbb_bin2onehot #(.WIDTH(WAY_N)) u_bin2oh_sbuway (.bin_i(sbu_dcu_data_req_way_m1), .onehot_o(sbu_dcu_data_req_wayoh));//将data的数据的way选择从二进制路索引转换为独热编码向量

// For SBU data request that access 64-bit value, the address for each RAM bank is the same.
// From req_addr[4:3], the double-word offset within the cacheline set.
assign sbu_data_req_addr_offs = sbu_dcu_data_req_addr_m1[4:3]; //double-word offset within the cacheline set.

// Determine the bank-pair containing the double-word being accessed.
// Due to the RAM bank corkscrew arrangement, the bank-pair is a function of the way and 
// the doubleword offset. The function rotate left the way based on the offset.
assign sbu_data_req_bank_pair = rotate_left_4bit(sbu_dcu_data_req_wayoh, sbu_data_req_addr_offs);  //Select the bank pair through doubleword offset and way

// SBU data request access 64-bit value, i.e., a single bank pair.
// When read/write low  half 32-bit, even bank (bank0/2/4/6) is accessed.
// When read/write high half 32-bit, odd  bank (bank1/3/5/7) is accessed.
assign sbu_data_access_ebank = sbu_dcu_data_req_type_m1 ? (|sbu_dcu_data_req_wstrb_m1[3:0]) : sbu_dcu_data_req_mask_m1[0]; //when read is low32bit, when write ebank select wr_low32bit
assign sbu_data_access_obank = sbu_dcu_data_req_type_m1 ? (|sbu_dcu_data_req_wstrb_m1[7:4]) : sbu_dcu_data_req_mask_m1[1]; //when read is low32bit, when write ebank select wr_low32bit

// CMO-zero write whole cacheline, and nonzero data_req write one bank_pair only.
assign sbu_data_req_ebank_cs = sbu_dcu_data_req_zero_m1 ? 4'b1111 : //write all 0
                                                          (sbu_data_req_bank_pair & {4{sbu_data_access_ebank}});  //bank_pair_index && ebank_vld
assign sbu_data_req_obank_cs = sbu_dcu_data_req_zero_m1 ? 4'b1111 : //write all 0
                                                          (sbu_data_req_bank_pair & {4{sbu_data_access_obank}});  //bank_pair_index && obank_vld

// SBU data request update tag RAM dirty bit.
assign sbu_data_req_tag_wdata[TAG_DIRTY_BIT] = 1'b1;  // dirty bit 
assign sbu_data_req_tag_wdata[TAG_VALID_BIT] = 1'b1;  // valid bit
assign sbu_data_req_tag_wdata[TAG_W-1:0]     = sbu_dcu_data_req_addr_m1[31 -: TAG_W]; //tag bit

//=================================================================================
// Pre-arbiter -- M1
//=================================================================================

// TAG RAM pre-arbiter priority:
// linefill(0) > store data req(1) > store tag check(2)

assign pab_tag_req[PAB_TAG_LFL] = lfl_wr_vld;   //linefill写入有效，表示有miss回填要写tag，优先级最高。
// For SBU data request, write tag only when cacheline tag is clean (sbu_dcu_data_req_status_m1).
assign pab_tag_req[PAB_TAG_STD] = sbu_dcu_data_req_vld_m1 & sbu_dcu_data_req_type_m1 & ~sbu_dcu_data_req_status_m1 & ~cmo_raw_req; //SBU数据请求有效 && 请求类型为写数据 && cacheline tag 必须是 clean 状态 && 没有CMO操作抢占。只有满足这些条件，才发起store data写tag请求
assign pab_tag_req[PAB_TAG_STC] = sbu_dcu_tag_req_vld_m1 & (~|sbu_dcu_tag_req_cmoop_m1) & ~cmo_raw_req;  //SBU tag请求有效 && 不是CMO操作 && 没有CMO操作抢占, 这是普通的store tag check请求，优先级最低

//只有在高优先级请求没有时，低优先级请求才会被授权。
assign pab_tag_grt[PAB_TAG_LFL] = pab_tag_req[PAB_TAG_LFL];
assign pab_tag_grt[PAB_TAG_STD] = pab_tag_req[PAB_TAG_STD] & ~pab_tag_req[PAB_TAG_LFL];
assign pab_tag_grt[PAB_TAG_STC] = pab_tag_req[PAB_TAG_STC] & ~pab_tag_req[PAB_TAG_STD] & ~pab_tag_req[PAB_TAG_LFL];

// DATA RAM pre-arbiter priority:
// linefill > store data req > evict read cacheline

assign pab_dat_req[PAB_DAT_LFL] = lfl_wr_vld; //linefill写入有效，优先级最高。
assign pab_dat_req[PAB_DAT_STD] = sbu_dcu_data_req_vld_m1 & ~cmo_raw_req; //SBU数据请求有效，且没有CMO操作，优先级第二。
assign pab_dat_req[PAB_DAT_EVC] = evc_rd_vld;  //evict buffer 读cacheline请求，优先级最低。

 //只有在高优先级请求没有时，低优先级请求才会被授权。
assign pab_dat_grt[PAB_DAT_LFL] = pab_dat_req[PAB_DAT_LFL];
assign pab_dat_grt[PAB_DAT_STD] = pab_dat_req[PAB_DAT_STD] & ~pab_dat_req[PAB_DAT_LFL];
assign pab_dat_grt[PAB_DAT_EVC] = pab_dat_req[PAB_DAT_EVC] & ~pab_dat_req[PAB_DAT_STD] & ~pab_dat_req[PAB_DAT_LFL];


//=================================================================================
// Pre-arbiter tag_ram interface -- M1
//=================================================================================


// Pre-arbiter tag RAM access mux 
//这是一个way one-hot信号，表示本次tag操作要访问哪个way。
assign pab_tag_bank_cs = {WAY_N{pab_tag_grt[PAB_TAG_LFL]}} & lfl_tag_bank_cs        |    //如果linefill被授权，选择lfl_tag_bank_cs。
                         {WAY_N{pab_tag_grt[PAB_TAG_STD]}} & sbu_dcu_data_req_wayoh |    //如果store data被授权，选择sbu_dcu_data_req_wayoh。
                         {WAY_N{pab_tag_grt[PAB_TAG_STC]}} & sbu_tag_req_bank_cs    ;    //如果store tag check被授权，选择sbu_tag_req_bank_cs。

assign pab_tag_setidx  = {SETIDX_W{pab_tag_grt[PAB_TAG_LFL]}} & lfl_addr[5+:SETIDX_W]                 | //linefill授权时，取lfl_addr的set部分。
                         {SETIDX_W{pab_tag_grt[PAB_TAG_STD]}} & sbu_dcu_data_req_addr_m1[5+:SETIDX_W] | //store data授权时，取sbu_dcu_data_req_addr_m1的set部分。
                         {SETIDX_W{pab_tag_grt[PAB_TAG_STC]}} & sbu_dcu_tag_req_addr_m1 [5+:SETIDX_W] ; //store tag check授权时，取sbu_dcu_tag_req_addr_m1的set部分。

assign pab_tag_wr      = pab_tag_grt[PAB_TAG_LFL] | //只有linefill或store data被授权时，才会对tag进行写操作。
                         pab_tag_grt[PAB_TAG_STD] ;

assign pab_tag_wdata = {TAG_FULL_W{pab_tag_grt[PAB_TAG_LFL]}} & lfl_tag_wdata         | //linefill授权时，写lfl_tag_wdata。
                       {TAG_FULL_W{pab_tag_grt[PAB_TAG_STD]}} & sbu_data_req_tag_wdata; //store data授权时，写sbu_data_req_tag_wdata。


// Pre-arbiter data RAM access mux 
//=================================================================================
// Pre-arbiter data_ram interface -- M1
//=================================================================================


assign pab_dat_ebank_cs = {WAY_N{pab_dat_grt[PAB_DAT_LFL]}} & lfl_dat_bank_cs       |   //linefill授权时，选择lfl_dat_bank_cs。
                          {WAY_N{pab_dat_grt[PAB_DAT_STD]}} & sbu_data_req_ebank_cs |   //store data授权时，选择sbu_data_req_ebank_cs。
                          {WAY_N{pab_dat_grt[PAB_DAT_EVC]}} & 4'b1111               ;   //evict授权时，所有way都选中（4'b1111），因为evict可能需要读出所有way的数据。

assign pab_dat_obank_cs = {WAY_N{pab_dat_grt[PAB_DAT_LFL]}} & lfl_dat_bank_cs       |   //linefill授权时，选择lfl_dat_bank_cs。
                          {WAY_N{pab_dat_grt[PAB_DAT_STD]}} & sbu_data_req_obank_cs |   //store data授权时，选择sbu_data_req_obank_cs。
                          {WAY_N{pab_dat_grt[PAB_DAT_EVC]}} & 4'b1111               ;   //evict授权时，所有way都选中（4'b1111），因为evict可能需要读出所有way的数据。

assign pab_dat_setidx   = {SETIDX_W{pab_dat_grt[PAB_DAT_LFL]}} & lfl_addr[5+:SETIDX_W]                 |    //linefill授权时，取lfl_addr的set部分。
                          {SETIDX_W{pab_dat_grt[PAB_DAT_STD]}} & sbu_dcu_data_req_addr_m1[5+:SETIDX_W] |    //store data授权时，取sbu_dcu_data_req_addr_m1的set部分。
                          {SETIDX_W{pab_dat_grt[PAB_DAT_EVC]}} & evc_setidx                            ;    //evict授权时，取evc_setidx。

//pab_data_bankXX_offs：决定bank内的offset（哪一小块数据）。
assign pab_data_bank01_offs = (pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1) ?  //如果是store data且不是zero操作，取请求地址的低位。
                                  sbu_dcu_data_req_addr_m1[4:3] :                       
                                  line_data_addr01              ;                       //否则（如linefill、zero等），取linefill相关的offset。

assign pab_data_bank23_offs = (pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1) ? 
                                  sbu_dcu_data_req_addr_m1[4:3] :
                                  line_data_addr23              ;

assign pab_data_bank45_offs = (pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1) ? 
                                  sbu_dcu_data_req_addr_m1[4:3] :
                                  line_data_addr45              ;

assign pab_data_bank67_offs = (pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1) ? 
                                  sbu_dcu_data_req_addr_m1[4:3] :
                                  line_data_addr67              ;

//只有linefill或store data（且是写操作）被授权时，才会对data RAM进行写操作。
assign pab_dat_wr           = pab_dat_grt[PAB_DAT_LFL] |
                              pab_dat_grt[PAB_DAT_STD] & sbu_dcu_data_req_type_m1;

for (genvar i=0; i<WAY_N; i++) begin : g_pab_wdata
  //linefill授权时，写lfl_dat_ebank/obank_wdata[i]。
  //store data授权且不是zero操作时，写sbu_dcu_ebank/obank_wdata。
  assign pab_dat_ebank_wdata[i] = {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_LFL]}} & lfl_dat_ebank_wdata[i]
                                | {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1}} & sbu_dcu_ebank_wdata ;
  
  assign pab_dat_obank_wdata[i] = {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_LFL]}} & lfl_dat_obank_wdata[i]
                                | {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_STD] & ~sbu_dcu_data_req_zero_m1}} & sbu_dcu_obank_wdata ;

  //linefill授权时，全部使能（全1）。
  //store data授权时，按sbu_data_req_ebank/obank_wen使能。
  assign pab_dat_ebank_wen[i]   = {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_LFL]}} & {DAT_RAM_D_W{1'b1}}
                                | {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_STD]}} & sbu_data_req_ebank_wen;

  assign pab_dat_obank_wen[i]   = {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_LFL]}} & {DAT_RAM_D_W{1'b1}}
                                | {DAT_RAM_D_W{pab_dat_grt[PAB_DAT_STD]}} & sbu_data_req_obank_wen;

end

//=================================================================================
// Main Arbiter -- M1
//=================================================================================

// Main arbiter priority:
// CMO/ECC > load > pre-arbiter

assign arb_req[ARB_CMO] = cmo_arb_req;                                                      //CMO/ECC请求信号，直接由cmo_arb_req决定。
assign arb_req[ARB_LD ] = ld_req_valid_m1[0] & ~cmo_raw_req;                                //load请求信号，只有在ld_req_valid_m1[0]有效且没有CMO原始请求（~cmo_raw_req）时才有效。
assign arb_req[ARB_PAB] = (|pab_tag_req) | (|pab_dat_req);                                  //pre-arbiter请求信号，只要pab_tag_req或pab_dat_req有任意一位有效即可

assign arb_grt[ARB_CMO] = arb_req[ARB_CMO];                                                 //只要有CMO/ECC请求，直接授予访问权（最高优先级）。
assign arb_grt[ARB_LD ] = arb_req[ARB_LD ] & ~arb_req[ARB_CMO];                             //只有在有load请求且没有CMO/ECC请求时才授予load。
assign arb_grt[ARB_PAB] = arb_req[ARB_PAB] & ~arb_req[ARB_LD ] & ~arb_req[ARB_CMO];         //只有在有PAB请求且没有load和CMO/ECC请求时才授予PAB（最低优先级）。


// Handshake req_rdy

assign ld0_grt                 = arb_grt[ARB_LD]; //ld0_grt：load0请求被主仲裁器授权（即本周期可以发起load0操作）。
// When load dual-issue is possible, either same-cacheline or micro-tag hit, load1 can be accepted.
assign ld1_grt                 = arb_grt[ARB_LD] & ld1_dual_iss_m1 &  //只有在主仲裁器授权load（arb_grt[ARB_LD]），且允许dual-issue（ld1_dual_iss_m1，即同一cacheline或micro-tag命中），且load1的bank资源可用时，load1才被接受。
                                    (ldxb_ebank_grt[1] | ldxb_obank_grt[1]);
//保证了同时tag_ram和data_ram的在同一个时刻同时仲裁
assign lfl_wr_rdy              = arb_grt[ARB_PAB] & pab_tag_grt[PAB_TAG_LFL];// 只有在主仲裁器授权pre-arbiter（arb_grt[ARB_PAB]），且linefill tag写入被授权（pab_tag_grt[PAB_TAG_LFL]）时，linefill写入才可以进行。

assign stc_grt                 = arb_grt[ARB_PAB] & pab_tag_grt[PAB_TAG_STC];// 只有在主仲裁器授权pre-arbiter，且store tag check被授权时，store tag check才可以进行。
assign stc_cmo_grt             = sbu_dcu_tag_req_vld_m1 & (|sbu_dcu_tag_req_cmoop_m1) & //当SBU发起tag请求且是CMO操作（sbu_dcu_tag_req_cmoop_m1非零），且cbom和calm状态机都空闲时，CMO的store tag check才可以进行。
                                 cbom_state_idle & calm_state_idle;
assign dcu_sbu_tag_req_rdy_m1  = stc_grt | stc_cmo_grt;                      //只要普通store tag check或CMO tag check有一个被授权，SBU的tag请求就可以被接受。

assign dcu_sbu_data_req_rdy_m1 = arb_grt[ARB_PAB] & pab_dat_grt[PAB_DAT_STD]; //只有主仲裁器授权pre-arbiter，且store data被授权时，SBU的数据请求才可以被接受。

assign evc_rd_rdy              = arb_grt[ARB_PAB] & pab_dat_grt[PAB_DAT_EVC]; //只有主仲裁器授权pre-arbiter，且evict buffer读被授权时，evict读才可以进行。

// prefetch only get grant and access tag RAM when no other requests exist.
assign prefetch_grt            = lsu_dcu_prefetch_valid & ~|arb_req; //只有在有prefetch请求且没有其他任何请求时，prefetch才会被授权访问tag RAM。


//=================================================================================
// RAM Interface
//=================================================================================


// Tag RAMs interface

assign dcu_ram_tag_cs   = {WAY_N{arb_grt[ARB_CMO]}} & cmo_tag_bank_cs       //CMO（Cache Management Operation）最高，直接用cmo_tag_bank_cs。
                        | ( ({WAY_N{arb_grt[ARB_LD ]}} & ld_tag_bank_cs  |  //其次是load、pre-arbiter（如linefill、store等）、prefetch，分别用各自的bank_cs
                             {WAY_N{arb_grt[ARB_PAB]}} & pab_tag_bank_cs |
                             {WAY_N{prefetch_grt    }} & 4'b1111         )  //prefetch时所有way都选中（4'b1111）。
                          &  {WAY_N{csr_dcache_en}} );                      //只有dcache使能（csr_dcache_en）时才允许访问。

// Here don't use aomux to avoid arb_grt[ARB_PAB], which involve load timing path.
//tag_wdata_preecc：写入Tag RAM的数据（ECC编码前）。
assign tag_wdata_preecc = arb_grt[ARB_CMO] ? cmo_tag_wdata   //只有CMO和pre-arbiter（PAB）操作会写tag，load和prefetch不写tag。
                                           : pab_tag_wdata;

for (genvar i=0; i<WAY_N; i++) begin : g_ram_tag
 
  assign dcu_ram_tag_wr[i]   = arb_grt[ARB_CMO] & cmo_tag_wr   //只有CMO或PAB被授权时才会写tag。  1:wr 0:rd
                             | arb_grt[ARB_PAB] & pab_tag_wr ; //其他都是读

  assign dcu_ram_tag_addr[i] = {SETIDX_W{arb_grt[ARB_CMO]}} & cmo_tag_setidx //根据授权类型选择不同的set索引。
                             | {SETIDX_W{arb_grt[ARB_LD ]}} & ld_tag_setidx
                             | {SETIDX_W{arb_grt[ARB_PAB]}} & pab_tag_setidx
                             | {SETIDX_W{prefetch_grt    }} & lsu_dcu_prefetch_addr[5+:SETIDX_W];

  assign dcu_ram_tag_wdata[i] = (arb_grt[ARB_CMO] & ecc_arb_req) ? ecc_tag_wdata[i] : //如果是CMO且需要ECC修正，写ecc_tag_wdata[i]。
                                                                   tag_wdata_postecc; //否则写tag_wdata_postecc（ECC编码后的数据）。
 
  assign dcu_ram_tag_wen[i]   = {TAG_RAM_D_W{1'b1}};          //第i个way的Tag RAM写使能（全使能,所有 bit 都可以写）

end

// data ram ebank and obank use same wr flag.
assign dcu_ram_data_wr = arb_grt[ARB_CMO] & cmo_dat_wr  //只有CMO或pre-arbiter（PAB）被授权时，才允许写操作。
                       | arb_grt[ARB_PAB] & pab_dat_wr; //load操作不写data RAM。

// Data RAMs even banks (bank 0/2/4/6) interface

assign dcu_ram_data0_cs      = {WAY_N{arb_grt[ARB_CMO]}} & cmo_dat_ebank_cs     //优先级：CMO > load > PAB
                             | ( ({WAY_N{arb_grt[ARB_LD ]}} & ld_dat_ebank_cs |
                                  {WAY_N{arb_grt[ARB_PAB]}} & pab_dat_ebank_cs)
                               &  {WAY_N{csr_dcache_en}} );                     //只有dcache使能时才允许load/PAB访问。

assign dcu_ram_data0_wr      = {WAY_N{dcu_ram_data_wr}};                        //每个way的读写，和dcu_ram_data_wr一致。
//每个bank的访问地址。CMO、load、PAB三种来源，优先级依次选择。PAB时，set索引和bank内offset拼接。
assign dcu_ram_data0_addr[0] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank01_offs} ;

assign dcu_ram_data0_addr[1] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank23_offs} ;

assign dcu_ram_data0_addr[2] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank45_offs} ;

assign dcu_ram_data0_addr[3] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_ebank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank67_offs} ;

assign dcu_ram_data0_wen     = arb_grt[ARB_CMO] ? {WAY_N{cmo_dat_wen}} //写掩码，CMO和PAB来源不同。
                                                : pab_dat_ebank_wen ;

assign dcu_ram_data0_wdata   = arb_grt[ARB_CMO] ? {WAY_N{cmo_dat_ebank_wdata}} //写入数据，CMO和PAB来源不同。
                                                : pab_dat_ebank_wdata ;

// Data RAMs odd banks (bank 1/3/5/7) interface

assign dcu_ram_data1_cs      = {WAY_N{arb_grt[ARB_CMO]}} & cmo_dat_obank_cs
                             | ( ({WAY_N{arb_grt[ARB_LD ]}} & ld_dat_obank_cs |
                                  {WAY_N{arb_grt[ARB_PAB]}} & pab_dat_obank_cs)
                               &  {WAY_N{csr_dcache_en}} );

assign dcu_ram_data1_wr      = {WAY_N{dcu_ram_data_wr}};


assign dcu_ram_data1_addr[0] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank01_offs} ;

assign dcu_ram_data1_addr[1] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank23_offs} ;

assign dcu_ram_data1_addr[2] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank45_offs} ;

assign dcu_ram_data1_addr[3] = {DAT_RAM_A_W{arb_grt[ARB_CMO]}} & cmo_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_LD ]}} & ld_dat_obank_addr
                             | {DAT_RAM_A_W{arb_grt[ARB_PAB]}} & {pab_dat_setidx, pab_data_bank67_offs} ;

assign dcu_ram_data1_wen     = arb_grt[ARB_CMO] ? {WAY_N{cmo_dat_wen}}
                                                : pab_dat_obank_wen ;

assign dcu_ram_data1_wdata   = arb_grt[ARB_CMO] ? {WAY_N{cmo_dat_obank_wdata}}
                                                : pab_dat_obank_wdata ;


//=================================================================================
// Pipeline - M2
//=================================================================================


/////////////////////
//rd type reg pipeline (load store pref)all rd req
/////////////////////

assign dff_tag_type_m2_set = arb_grt[ARB_LD] | stc_grt | prefetch_grt;    //只要有load、store请求或prefetch请求被仲裁器授予，就需要设置tag类型寄存器。
assign dff_tag_type_m2_clr = |dff_tag_type_m2_q;                          //如果寄存器当前有任意一位为1（即之前存过tag类型），则需要清除（clr）
assign dff_tag_type_m2_en  = dff_tag_type_m2_set | dff_tag_type_m2_clr;   //只要需要设置或清除，就使能（en）寄存器更新。
assign dff_tag_type_m2_d[TAG_TYPE_LD ] = arb_grt[ARB_LD];                 //根据是哪种请求被授予，对应的tag类型位被置1，其余为0。
assign dff_tag_type_m2_d[TAG_TYPE_STC] = stc_grt;
assign dff_tag_type_m2_d[TAG_TYPE_PRF] = prefetch_grt;

`WDFFER(dff_tag_type_m2_q, dff_tag_type_m2_d, dff_tag_type_m2_en, clk, rst_n) //当dff_tag_type_m2_en为1时，dff_tag_type_m2_q会被更新为dff_tag_type_m2_d的值。


/////////////////////
// info reg pipeline
/////////////////////


assign tag_info_sel[TAG_TYPE_LD ] = dff_tag_type_m2_d[TAG_TYPE_LD ]; //这三行代码将dff_tag_type_m2_d（即本周期被仲裁的tag类型）直接赋值给tag_info_sel的对应位。
assign tag_info_sel[TAG_TYPE_STC] = dff_tag_type_m2_d[TAG_TYPE_STC]; //这样tag_info_sel就表示当前是哪种类型的请求（load、store-conditional、prefetch）被选中。
assign tag_info_sel[TAG_TYPE_PRF] = dff_tag_type_m2_d[TAG_TYPE_PRF];
 //dff_addr_m2_en为1时，表示需要更新M2阶段的地址寄存器。
assign dff_addr_m2_en   = dff_tag_type_m2_set | cbom_state_rdtag;                    //只要有新的tag类型请求被仲裁（dff_tag_type_m2_set），或者CBOM（cache block management）状态机需要读tag（cbom_state_rdtag），就会使能地址寄存器的写入。
assign dff_addr_m2_d    = {32{tag_info_sel[TAG_TYPE_LD ]}} & ld_req_addr_m1[0]       //如果是load请求，选用ld_req_addr_m1[0]。
                        | {32{tag_info_sel[TAG_TYPE_STC]}} & sbu_dcu_tag_req_addr_m1 //如果是store-conditional请求，选用sbu_dcu_tag_req_addr_m1。
                        | {32{tag_info_sel[TAG_TYPE_PRF]}} & lsu_dcu_prefetch_addr   //如果是prefetch请求，选用lsu_dcu_prefetch_addr。
                        | {32{cbom_state_rdtag}}           & cbom_addr_m1;           //如果是CBOM状态机读tag，选用cbom_addr_m1。




assign dff_info_m2_en   = dff_tag_type_m2_set;                                       //解释： 只有在有新的tag类型请求(没有cmo)被仲裁时，才使能info相关寄存器的写入

// Size is only used for nc/device load request
assign dff_size_m2_d    = {2{tag_info_sel[TAG_TYPE_LD ]}} & ld_req_size_m1[0];       //仅当请求类型为加载（TAG_TYPE_LD）时，才传递ld_req_size_m1[0]。

assign dff_excl_m2_d    = tag_info_sel[TAG_TYPE_LD ] & ld_req_excl_m1[0];            //仅load请求时，传递独占访问信号。

assign dff_priv_m2_d    = tag_info_sel[TAG_TYPE_LD ] & (|core_priv_mode)             //加载请求时，使用CPU核心特权模式。
                        | tag_info_sel[TAG_TYPE_STC] & sbu_dcu_tag_req_priv_m1;      //存储请求（STC）时，使用SBU的特权信号。

assign dff_master_m2_d  = tag_info_sel[TAG_TYPE_LD ] & ld_req_master_m1[0]           //用LSU主控ID。
                        | tag_info_sel[TAG_TYPE_STC] & sbu_dcu_tag_req_master_m1     //存储请求：用SBU主控ID。
                        | cbom_state_rdtag           & dff_cbom_master_q;            //CBO操作：用CBO主控ID。

assign dff_idemp_m2_d   = tag_info_sel[TAG_TYPE_LD ] & ld_req_idempotency_m1[0]       //加载请求：用LSU幂等性。
                        | tag_info_sel[TAG_TYPE_STC] & sbu_dcu_tag_req_idempotency_m1 //存储请求：用SBU幂等性。
                        | tag_info_sel[TAG_TYPE_PRF] ;                                //预取请求：直接为1（预取总是幂等）。

assign dff_ima_m2_d     = {3{tag_info_sel[TAG_TYPE_LD ]}} & ld_req_inner_ma_m1[0]         //用LSU内部内存属性。
                        | {3{tag_info_sel[TAG_TYPE_STC]}} & sbu_dcu_tag_req_inner_ma_m1 ; //用SBU内部内存属性。

assign dff_oma_m2_d     = {3{tag_info_sel[TAG_TYPE_LD ]}} & ld_req_outer_ma_m1[0]       //用LSU外部内存属性。
                        | {3{tag_info_sel[TAG_TYPE_STC]}} & sbu_dcu_tag_req_outer_ma_m1 //用SBU外部内存属性。
                        | {3{tag_info_sel[TAG_TYPE_PRF]}} & lsu_dcu_prefetch_outer_ma ; //用预取外部内存属性。

// Align lsqid and sbqid to same width, then we can store these id uniformly.
//将加载请求的lsqid和存储请求的sbqid对齐到同样的宽度（ID_EXT_W），便于后续统一处理。
assign dff_tag_id_m2_d  = {ID_EXT_W{tag_info_sel[TAG_TYPE_LD ]}} & ld_req_id_m1[0]                                            //直接用ld_req_id_m1[0]（已是对齐宽度）。
                        | {ID_EXT_W{tag_info_sel[TAG_TYPE_STC]}} & { {ID_EXT_W-SBQID_W{1'b0}}, sbu_dcu_tag_req_sbqid_m1 };    //高位补零，使sbu_dcu_tag_req_sbqid_m1扩展到ID_EXT_W位。


 //当en为1时，在时钟上升沿将d的值存入q，否则保持原值。
`WDFFER(dff_addr_m2_q  , dff_addr_m2_d  , dff_addr_m2_en, clk, rst_n)
`WDFFER(dff_size_m2_q  , dff_size_m2_d  , dff_info_m2_en, clk, rst_n)
`WDFFER(dff_excl_m2_q  , dff_excl_m2_d  , dff_info_m2_en, clk, rst_n)
`WDFFER(dff_priv_m2_q  , dff_priv_m2_d  , dff_info_m2_en, clk, rst_n)
`WDFFER(dff_master_m2_q, dff_master_m2_d, dff_info_m2_en, clk, rst_n) //标识发起本次访问请求的主控（master）编号或ID。
`WDFFER(dff_idemp_m2_q , dff_idemp_m2_d , dff_info_m2_en, clk, rst_n) //0: Non-idempotency (device); 1: idempotency (non-device)
`WDFFER(dff_ima_m2_q   , dff_ima_m2_d   , dff_info_m2_en, clk, rst_n) //是否可缓存（cacheable）,是否写分配（write-allocate）,是否读分配（read-allocate）
`WDFFER(dff_oma_m2_q   , dff_oma_m2_d   , dff_info_m2_en, clk, rst_n) //访问主内存时是否缓存, 外部总线上的缓存一致性策略, dvice
`WDFFER(dff_tag_id_m2_q, dff_tag_id_m2_d, dff_info_m2_en, clk, rst_n)

/////////////////////
//rd data type reg pipeline(load rd + store wr + evict rd)
/////////////////////

assign dff_dat_type_m2_en              = (|arb_req) | (|dff_dat_type_m2_q);  //只要有仲裁请求（arb_req有任意一位为1），或者当前Q有类型(清空寄存器)，就使能数据类型寄存器的更新。
assign dff_dat_type_m2_d[DAT_TYPE_LD]  = arb_grt[ARB_LD];                    //如果仲裁器授予了加载请求（arb_grt[ARB_LD]为1），则dff_dat_type_m2_d的DAT_TYPE_LD位为1。
assign dff_dat_type_m2_d[DAT_TYPE_STD] = dcu_sbu_data_req_rdy_m1;            //如果数据请求准备好（dcu_sbu_data_req_rdy_m1为1），则dff_dat_type_m2_d的DAT_TYPE_STD位为1。
assign dff_dat_type_m2_d[DAT_TYPE_EVC] = evc_rd_rdy;                         //用带使能的D触发器锁存数据类型信号。只有dff_dat_type_m2_en为1时，才会更新dff_dat_type_m2_q的值。

`WDFFER(dff_dat_type_m2_q, dff_dat_type_m2_d, dff_dat_type_m2_en, clk, rst_n)

/////////////////////
// data reg pipeline
/////////////////////

assign dff_dat_info_m2_en    = (|arb_req);                  //只要有仲裁请求（arb_req有任意一位为1），就使能数据相关信息寄存器的更新。
assign dff_dat_rd_m2_d       = ~dcu_ram_data_wr;            //数据RAM的读使能信号，等于写使能信号的取反（ebank和obank共用写使能标志）。
assign dff_samecl_m2_d       = ld_req_samecl_m1 & ld1_grt;  //dff_samecl_m2_d为1，表示load1请求被仲裁且与load0在同一cacheline。
assign dff_ld1_hit_utag_m2_d = ld1_hit_utag_m1  & ld1_grt;  //dff_ld1_hit_utag_m2_d为1，表示load1命中utag且被仲裁。
assign dff_ld1_addr_m2_d     = ld_req_addr_m1[1];           //dff_ld1_addr_m2_d等于load1请求的地址。

//这条语句将不同类型的数据请求ID对齐到同样的宽度（ID_EXT_W），便于后续统一处理。
assign dff_dat_id_m2_d  = {ID_EXT_W{dff_dat_type_m2_d[DAT_TYPE_LD ]}} & ld_req_id_m1[1] //   如果是load类型（dff_dat_type_m2_d[DAT_TYPE_LD]为1）：直接用ld_req_id_m1[1]。                             
                        | {ID_EXT_W{dff_dat_type_m2_d[DAT_TYPE_STD]}} & { {ID_EXT_W-SBQID_W{1'b0}}, sbu_dcu_data_req_sbqid_m1 }  //如果是store类型（dff_dat_type_m2_d[DAT_TYPE_STD]为1）：高位补零，使sbu_dcu_data_req_sbqid_m1扩展到ID_EXT_W位。
                        | {ID_EXT_W{dff_dat_type_m2_d[DAT_TYPE_EVC]}} & { {ID_EXT_W-MB_ID_W{1'b0}}, evc_id                    }; //如果是eviction类型（dff_dat_type_m2_d[DAT_TYPE_EVC]为1）：高位补零，使evc_id扩展到ID_EXT_W位

// dff_dat_way_m2_q have different meaning for each access type:
//  - For store data read request and load micro-tag hit: used as bank selection to mux data, and already rotated with addr offset.
//  - For evction full cacheline access: used as registered way that accessed in M1.
assign dff_dat_way_m2_d  = {WAY_N{dcu_sbu_data_req_rdy_m1}} & sbu_data_req_bank_pair | //如果是store数据请求（dcu_sbu_data_req_rdy_m1为1），用sbu_data_req_bank_pair。
                           {WAY_N{evc_rd_rdy             }} & line_data_way          | //如果是eviction（驱逐）请求（evc_rd_rdy为1），用line_data_way。
                           {WAY_N{ld1_hit_utag_m1        }} & utag_ld1_bank_pair_m1  ; //如果是load1命中utag（ld1_hit_utag_m1为1），用utag_ld1_bank_pair_m1。
//data samecl, load1_hit, ld1_addr, data_id, data_way  ->   M2
`WDFFER(dff_dat_rd_m2_q      , dff_dat_rd_m2_d      , dff_dat_info_m2_en, clk, rst_n)
`WDFFER(dff_samecl_m2_q      , dff_samecl_m2_d      , dff_dat_info_m2_en, clk, rst_n)
`WDFFER(dff_ld1_hit_utag_m2_q, dff_ld1_hit_utag_m2_d, dff_dat_info_m2_en, clk, rst_n)
`WDFFER(dff_ld1_addr_m2_q    , dff_ld1_addr_m2_d    , dff_dat_info_m2_en, clk, rst_n)
`WDFFER(dff_dat_id_m2_q      , dff_dat_id_m2_d      , dff_dat_info_m2_en, clk, rst_n)
`WDFFER(dff_dat_way_m2_q     , dff_dat_way_m2_d     , dff_dat_info_m2_en, clk, rst_n)


/////////////////////
// ma pipeline
/////////////////////


// Memory-Attribute Update in M2
// LSU send speculative ma in M1 (4'b1111), and update real ma in M2. 
// SBU always send accurate ma in M1.
//ld_src_m2
// 0: ld request from VPU
// 1: ld request from LSU
assign real_idemp_m2 = (dff_dat_type_m2_q[DAT_TYPE_LD] & lsu_dcu_ld_req_ma_update_m2[0] & ld_src_m2)  //如果当前是load类型，并且需要更新ma（lsu_dcu_ld_req_ma_update_m2[0]为1），且是load来源（ld_src_m2为1），
                       ? lsu_dcu_ld_req_idempotency_m2[0]
                       : dff_idemp_m2_q;  //否则，使用之前寄存的dff_idemp_m2_q。

// When dcache disable, all cacheable transactions will be treated as non-cacheable and be sent to BMU
assign real_ima_m2   = {3{csr_dcache_en}} & //如果数据缓存使能（csr_dcache_en为1），
                       ( (dff_dat_type_m2_q[DAT_TYPE_LD] & lsu_dcu_ld_req_ma_update_m2[0] & ld_src_m2) //并且当前是load类型且需要更新inner_ma且是load来源，
                         ? lsu_dcu_ld_req_inner_ma_m2[0] //使用 LSU 发送来的 m2 的属性
                         : dff_ima_m2_q ); //否则，使用之前寄存的dff_ima_m2_q。

assign real_oma_m2   = (dff_dat_type_m2_q[DAT_TYPE_LD] & lsu_dcu_ld_req_ma_update_m2[0] & ld_src_m2) //并且当前是load类型且需要更新outer_ma且是load来源，
                       ? lsu_dcu_ld_req_outer_ma_m2[0] //使用 LSU 发送来的 m2 的属性
                       : dff_oma_m2_q; //否则，使用之前寄存的dff_ima_m2_q。


/////////////////////////
// flush and real ld req
/////////////////////////


// Load flush m2 pipeline stage
// Only used in load response logic, so NO dff_tag_type_m2_q[TAG_TYPE_LD] logic here.
 //LSU发起了flush操作（lsu_dcu_flush_valid为1） && 当前load0的流水线ID比flush的ID“更年轻”（id_younger(...)为1） && 当前是load来源（ld_src_m2为1）
assign flush_ppln_ld0_m2 = lsu_dcu_flush_valid & id_younger(dff_tag_id_m2_q[LSQID_W-1:0], lsu_dcu_flush_lsqid) & ld_src_m2;
assign flush_ppln_ld1_m2 = lsu_dcu_flush_valid & id_younger(dff_dat_id_m2_q[LSQID_W-1:0], lsu_dcu_flush_lsqid) & ld_src_m2;

// Load request valid exclude load flush and load cancel
// 当前是load类型请求（dff_tag_type_m2_q[TAG_TYPE_LD]为1） && load0请求没有被取消（~ld_req_cancel_m2[0]为1） && load0请求没有被flush无效化（~flush_ppln_ld0_m2为1）
assign ld0_real_vld_m2 = dff_tag_type_m2_q[TAG_TYPE_LD] & ~ld_req_cancel_m2[0] & ~flush_ppln_ld0_m2;
assign ld1_real_vld_m2 = dff_tag_type_m2_q[TAG_TYPE_LD] & ~ld_req_cancel_m2[1] & ~flush_ppln_ld1_m2;


//=================================================================================
// Pipeline - M3
//=================================================================================

/////////////////////////
// STD pipeline
/////////////////////////

assign dff_std_vld_m3_set = dff_dat_type_m2_q[DAT_TYPE_STD];        //如果M2阶段的数据类型是STD（store data），则M3阶段需要设置STD有效信号。
assign dff_std_vld_m3_clr = dff_std_vld_m3_q;                       //如果STD有效信号已经被置位（即dff_std_vld_m3_q为1），则需要清除（clr）该信号。
assign dff_std_vld_m3_en  = dff_std_vld_m3_set | dff_std_vld_m3_clr; //只要需要设置或清除STD有效信号，就使能STD有效信号的寄存器更新。
assign dff_std_vld_m3_d   = dff_std_vld_m3_set ;                     //STD有效信号的下一个值等于是否需要设置STD有效信号。

`WDFFER(dff_std_vld_m3_q, dff_std_vld_m3_d, dff_std_vld_m3_en, clk, rst_n)  //用带使能的D触发器锁存STD有效信号。只有dff_std_vld_m3_en为1时，才会更新dff_std_vld_m3_q的值。

//////////////////////////
// Id pipeline
//////////////////////////

assign dff_dat_id_m3_en = dff_dat_type_m2_q[DAT_TYPE_STD] |      //只要M2阶段是STD或LD（load data）类型，就使能数据ID寄存器的更新。
                          dff_dat_type_m2_q[DAT_TYPE_LD ] ;
assign dff_dat_id_m3_d  = dff_dat_id_m2_q;      //数据ID的下一个值等于M2阶段的数据ID。

`WDFFER(dff_dat_id_m3_q, dff_dat_id_m3_d, dff_dat_id_m3_en, clk, rst_n) //用带使能的D触发器锁存数据ID。只有dff_dat_id_m3_en为1时，才会更新dff_dat_id_m3_q的值。

// ECC code is registered separately in ECC section.

//////////////////////////
// data pipeline
//////////////////////////

assign dff_data_m3_en      = (dff_dat_type_m2_q[DAT_TYPE_STD] |                     //只有M2阶段是STD或LD类型，并且数据读使能信号为1时，才使能数据寄存器的更新。
                              dff_dat_type_m2_q[DAT_TYPE_LD ] ) & dff_dat_rd_m2_q;
// dff_ebank_data_m3_d and dff_obank_data_m3_d is inside ECC branch.

`WDFFER(dff_ebank_data_m3_q, dff_ebank_data_m3_d, dff_data_m3_en, clk, rst_n) //对ebank纠正后的数据进行打拍
`WDFFER(dff_obank_data_m3_q, dff_obank_data_m3_d, dff_data_m3_en, clk, rst_n)

//=================================================================================
// Hit gen -- M2
//=================================================================================

// All tag rams are accessed together when read them.
assign tag_ram_read_m1         = dcu_ram_tag_cs & ~dcu_ram_tag_wr; //所有标签RAM在读取时是一起访问的。 标签RAM读取信号为1的条件：标签RAM片选有效且不是写操作。

assign dff_tag_ram_read_m2_en  = (|tag_ram_read_m1      ) |  //只要M1阶段有任何标签RAM读取操作，或者M2阶段的标签RAM读取寄存器有值，就使能M2阶段寄存器的更新。
                                 (|dff_tag_ram_read_m2_q) ;
assign dff_tag_ram_read_m2_d   = {WAY_N{|tag_ram_read_m1}} & tag_ram_read_m1; //M2阶段标签RAM读取寄存器的输入值等于M1阶段的读取信号。

`WDFFER(dff_tag_ram_read_m2_q, dff_tag_ram_read_m2_d, dff_tag_ram_read_m2_en, clk, rst_n) //用带使能的D触发器锁存标签RAM读取信号到M2阶段。

// Generate the hit way, from Tag RAMs lookup in M2.(从M2阶段的标签RAM查找结果生成命中路信息。)
// The hitgen way info is only used for load0 and SBU tag_chk requests.(命中路信息只用于load0和SBU标签检查请求。)
// For dual-issue load non-same-cacheline, the hit way may be from micro-tag.(对于双发射load非同缓存行情况，命中路可能来自micro-tag。)
// For SBU data read requests, the hit way directly from the SBU.(对于SBU数据读请求，命中路直接来自SBU。)

for (genvar i=0; i<WAY_N; i++) begin : g_hitgen //对每个缓存路（way）进行标签比较。

  assign tag_rdata_valid_bit[i] = ram_tag_rdata[i][TAG_VALID_BIT]; //TAG_VALID_BIT = 19
  assign tag_rdata_dirty_bit[i] = ram_tag_rdata[i][TAG_DIRTY_BIT]; //TAG_DIRTY_BIT = 20
  assign tag_rdata_tag_bits [i] = ram_tag_rdata[i][TAG_W-1:0]; //TAG_W = 19

  assign tag_way_cmp[i] = tag_rdata_valid_bit[i] & (tag_rdata_tag_bits[i] == dff_addr_m2_q[31-:TAG_W]);

end

// The flag that tag rams are read in M1 stage must be registered to M2 as hit control,
// to filter out unrelated ramdom rdata.
assign tag_raw_hit  = |(tag_way_cmp & dff_tag_ram_read_m2_q) & csr_dcache_en;  //标签比较结果，每一位代表一个缓存路(way)的标签是否匹配 && M2阶段寄存的信号，表示标签RAM在M1阶段是否被读取 && 数据缓存使能位
assign tag_raw_miss = ~tag_raw_hit | hit_eb_as_miss_m2; //如果不是原始命中，或命中了正在被驱逐miss的数据，则认为是原始缺失

// Load[0] hit evict buffer with only eb_done status is regarded as miss.
// Other hit eb cases are ignored, i.e., use hit result directly.
// Detail sheet refer to 'Handling hit evict buffer'.
assign ld0_tag_is_hit = tag_raw_hit & dff_tag_type_m2_q[TAG_TYPE_LD ] & ~hit_eb_as_miss_m2; //原始命中 + 操作类型为加载 + 不命中被视为缺失的驱逐缓冲区
// STC hit evict buffer means either replay or regard as miss. Both cases are not hit.
assign stc_tag_is_hit = tag_raw_hit & dff_tag_type_m2_q[TAG_TYPE_STC] & ~hit_eb_m2; //对于STC操作，任何命中驱逐缓冲区的情况都不算作命中

// tag_is_miss is used for refill and mb_push, so exclude all repaly situations.
// Here hit_eb_as_replay_m2 is not excluded because hit_eb means tag_is_hit (tag is not invalidated when evict cacheline).
assign tag_is_miss  = tag_raw_miss & ~mb_full & ~all_way_locked & ~hit_mb_m2 & ~tag_ecc_bang_m2; //必须是原始缺失 && 缺失缓冲区不能满 && 所有缓存路不能都被锁定 && 不能命中缺失缓冲区 && 标签不能有ECC错误

 //.NUM(WAY_N): 输入数量，等于缓存路数（如4路） 
 //.WIDTH(1): 数据宽度为1位（因为脏位是1位信号）
 //输入信号 : 输入数据数组，包含所有缓存路的脏位
 //选择信号 : 独热编码选择命中的 way,前面标签比较的结果,例如：{0, 0, 1, 0} 表示第2路命中
 //输出信号 : 选择出的命中路的脏位,如果第2路命中，则输出 tag_rdata_dirty_bit[2] 的值
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(1)) u_aomux_hit_dty (.data_arr(tag_rdata_dirty_bit), .sel_in_onehot0(tag_way_cmp), .data_sel(tag_hit_dirty_bit));

//=================================================================================
// Hit mux M2 Data -- M2
//=================================================================================

// Load requests read one/two 32-bit data value from the dat RAMs.
// For load0 request, the data way always from Tag hitgen result.
// For load1 request,
//  - Same cacheline case: the data way also from Tag hitgen result.
//  - Micro-tag hit case: the data way from registered 'dff_dat_way_m2_q'.
//
// The SBU read up to 64-bits data value from a bank pair (Only when ECC enabled).
// The SBU data way from registered 'dff_dat_way_m2_q', which is already
// rotated depend on way bitmap and address[4:3].

// Hitgen situation load request offset for even-bank and odd-bank,
// Used to rotate way bitmap to get bank selction.

assign dat_ebank_dword_offs = {2{~dff_addr_m2_q[2]}} & dff_addr_m2_q[4:3]      //load0 使用 load0 m2 的地址, 当地址第2位为0（偶数）时，等于地址[4:3]；否则为0
                            | {2{ dff_addr_m2_q[2]}} & dff_ld1_addr_m2_q[4:3]; //load1 使用 load1 m2 的地址, 当地址第2位为1（奇数）时，等于地址[4:3]；否则为0

assign dat_obank_dword_offs = {2{ dff_addr_m2_q[2]}} & dff_addr_m2_q[4:3]      //load0 使用 load0 m2 的地址, 当地址第2位为0（偶数）时，等于地址[4:3]；否则为0
                            | {2{~dff_addr_m2_q[2]}} & dff_ld1_addr_m2_q[4:3]; //load0 使用 load0 m2 的地址, 当地址第2位为1（奇数）时，等于地址[4:3]；否则为0

// Bank_select from Tag RAM compare hitgen results.
assign hg_dat_ebank_sel = rotate_left_4bit(tag_way_cmp, dat_ebank_dword_offs); //根据偶数bank偏移量旋转标签比较结果，得到偶数bank选择信号
assign hg_dat_obank_sel = rotate_left_4bit(tag_way_cmp, dat_obank_dword_offs); //根据奇数bank偏移量旋转标签比较结果，得到奇数bank选择信号

// Determine use non-hitgen bank_sel or hitgen bank_sel.
// If data bank type is load and dff_ld1_hit_utag_m2_q is non-zero, means utag hit.
// If data bank type is load, addr[2] corresponding to load0 address offset
//  - addr[2]==0, load0 access even bank, load1 access odd bank. If load1 hit utag, obank pick no_hitgen way info.
//  - addr[2]==1, load0 access odd bank, load1 access even bank. If load1 hit utag, ebank pick no_hitgen way info.
/*
决定使用非命中生成还是命中生成的bank选择：这段代码的目的是确定数据bank在选择数据路径时，是使用常规的标签命中生成结果，还是使用非命中生成的逻辑
微标签命中条件：当数据类型是加载且dff_ld1_hit_utag_m2_q非零时，表示发生了微标签命中
地址位与bank访问关系：
addr[2]==0：load0访问偶数bank，load1访问奇数bank。如果load1命中微标签，奇数bank使用非命中生成方式
addr[2]==1：load0访问奇数bank，load1访问偶数bank。如果load1命中微标签，偶数bank使用非命中生成方式
*/
//当STD和LD时，偶数bank使用非命中生成逻辑
assign data_ebank_pick_nohg = dff_dat_type_m2_q[DAT_TYPE_LD ] & ~dff_ld1_addr_m2_q[2] & dff_ld1_hit_utag_m2_q //当load1访问奇数bank且命中微标签时，偶数bank使用非命中生成逻辑
                            | dff_dat_type_m2_q[DAT_TYPE_STD] & dff_dat_rd_m2_q;                              //存储操作时，偶数bank也使用非命中生成逻辑

assign data_obank_pick_nohg = dff_dat_type_m2_q[DAT_TYPE_LD ] & dff_ld1_addr_m2_q[2] & dff_ld1_hit_utag_m2_q  //当load1访问偶数bank且命中微标签时，奇数bank使用非命中生成逻辑
                            | dff_dat_type_m2_q[DAT_TYPE_STD] & dff_dat_rd_m2_q;                              //存储操作时，奇数bank也使用非命中生成逻辑  

assign dat_ebank_sel = data_ebank_pick_nohg ? dff_dat_way_m2_q :
                                              hg_dat_ebank_sel;
 
assign dat_obank_sel = data_obank_pick_nohg ? dff_dat_way_m2_q :
                                              hg_dat_obank_sel;


//
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(DAT_RAM_D_W)) u_aomux_word_e (.data_arr(ram_data0_rdata), .sel_in_onehot0(dat_ebank_sel), .data_sel(full_data_ebank_m2)); //奇数字节选择器从 ram_data1_rdata(ram输出) 中选择数据
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(DAT_RAM_D_W)) u_aomux_word_o (.data_arr(ram_data1_rdata), .sel_in_onehot0(dat_obank_sel), .data_sel(full_data_obank_m2)); //偶数字节选择器从 ram_data0_rdata(ram输出) 中选择数据


//=================================================================================
// Miss Process
//=================================================================================


// Filter hitgen with nc/device. //在标签命中生成逻辑中过滤掉非缓存（nc）和设备（device）类型的访问
// SBU send tag_chk request to DCU only when ma is cacheable/non-device, //：SBU只有在内存属性是可缓存/非设备时才向DCU发送标签检查请求
// if not, send nc/device request to BMU directly. // 如果不是可缓存/设备类型，直接发送到BMU处理
assign no_ncdvc_m2    = real_idemp_m2 & real_ima_m2[MA_CACHE]; //M2阶段的"非不可缓存/非设备"信号,只有当请求是cache且内存属性是可缓存时，才认为不是不可缓存/非设备
assign cache_alloc_m2 = no_ncdvc_m2 & (ld0_real_vld_m2                 & real_ima_m2[MA_RD_ALLOC] |  //有效的load0请求 + 内存属性允许读分配
                                       dff_tag_type_m2_q[TAG_TYPE_STC] & real_ima_m2[MA_WR_ALLOC] ); //STC操作 + 内存属性允许写分配

// Determine need refill.
// Following cases doesn't need refill:
//  - load with non-read-allocate, which will be sent to BMU just like nc/device.
//  - SBU tag_chk with non-write-allocate is directly ignored.
//  - flushed load.   (already involved in ld0_real_vld_m2)
//  - cancelled load. (already involved in ld0_real_vld_m2)
//  - replay cases: missbuf_full, all_way_lock, hit_mb, hit_eb_as_replay (already involved in tag_is_miss)

assign need_refill     = tag_is_miss & csr_dcache_en &     //标签缺失 && 数据缓存使能 && 
                       ( cache_alloc_m2                    //可分配 || 预取请求
                       | dff_tag_type_m2_q[TAG_TYPE_PRF] ); 


// M2 request that hit DCache must NOT be nc/device, this is monitored with assertion.
assign hit_update_plru = (ld0_tag_is_hit | stc_tag_is_hit |  //req tag hit 
                          tag_raw_hit & dff_tag_type_m2_q[TAG_TYPE_PRF]) & ~tag_ecc_bang_m2; //prefetch hit

assign tag_way_all_vld = &(tag_rdata_valid_bit | locked_way_m2); //valid or select way
assign tag_way_invld   = ~(tag_rdata_valid_bit | locked_way_m2); //invalid & noselect way

wing_cbb_priority_queue #(  //从左到右，第一个1的位置， 输入1110，输出则为0010.用于选择第一个有效way
  .MODE  ( 0     ), // MODE=0: ff1 from LSB.// 选择优先级方向，0=从低位（LSB）优先，1=从高位（MSB）优先
  .WIDTH ( WAY_N ), // 输入/输出信号宽度
  .LOCK  ( 0     )  // 是否支持lock功能
) u_ff1_victim_way (
  .clk      ( clk              ),
  .rst_n    ( rst_n            ),
  .lock     ( 1'b0             ), 
  .vec_i    ( tag_way_invld    ), 
  .onehot_o ( invld_victim_way )  //vec_i = 4'b0110，则onehot_mask = 4'b0010
);

// There are two plru_valid cases:
// - tag hit updates plru status.
// - need_refill (tag miss and allocate) gets victim way.
assign plru_valid = hit_update_plru | (need_refill & tag_way_all_vld); //命中更新plru || 需要填充 && 所有way都有效， 需要填充 && 所有way都没有效时不使用plru策略

wing_cbb_plru_array #(
  .PORT_N  ( 1     ), //端口数量（固定为1）
  .WAY_N   ( WAY_N ), //路数（固定为4路）
  .SET_N   ( SET_N )  //组数（512组）
) u_plru_victim (
  .clk        ( clk                         ),
  .rst_n      ( rst_n                       ),
  .valid      ( plru_valid                  ), //enable
  .hit_miss   ( hit_update_plru             ), //hit updata plru
  .index      ( dff_addr_m2_q[5+:SETIDX_W]  ), //缓存组索引
  .hit_way    ( tag_way_cmp                 ), //命中路位图
  .lock_way   ( locked_way_m2               ), //锁定路位图

  .rplc_way_o ( plru_victim_way             )  //替换路位图
);

assign victim_way = calm_state_clean ? calm_clean_way   : //select all dirty way to clean
                    cbom_state_chk   ? tag_way_cmp      : //select cmo hit way 
                    tag_way_all_vld  ? plru_victim_way  : //select victim way in 4-way valid way 
                                       invld_victim_way ; //invld victim way

// Determine evict
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(1    )) u_aomux_vtm_vld (.data_arr(tag_rdata_valid_bit), .sel_in_onehot0(victim_way), .data_sel(victim_way_valid_bit)); //select victim valid
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(1    )) u_aomux_vtm_dty (.data_arr(tag_rdata_dirty_bit), .sel_in_onehot0(victim_way), .data_sel(victim_way_dirty_bit)); //select victim dirty 
wing_cbb_aomux #(.NUM(WAY_N), .WIDTH(TAG_W)) u_aomux_vtm_tag (.data_arr(tag_rdata_tag_bits ), .sel_in_onehot0(victim_way), .data_sel(victim_way_tag_bits )); //select victim tag 


//=================================================================================
// Way lock from missbuf and sbu
//=================================================================================


for (genvar i=0; i<SBQ_ENT_N; i++) begin : g_mb_lock

  assign sbu_ent_eq_setidx_m2[i] = (dff_addr_m2_q[5+:SETIDX_W] == sbu_dcu_waylock_index[i]); //set匹配
  assign sbu_ent_lock_vld    [i] = sbu_dcu_waylock_vld[i] & sbu_ent_eq_setidx_m2[i]; //只有当way lock有效且index匹配时，才认为第i个SBU entry的way lock有效

  wing_cbb_bin2onehot #(.WIDTH(WAY_N)) u_bin2oh_sbqwl (.bin_i(sbu_dcu_waylock_way[i]), .onehot_o(sbu_ent_lock_way[i])); //way_index to way_onehot 

end

// Here sbu_ent_lock_vld and mb_ent_lock_vld are NOT actually onehot,
// we just use aomux OR logic to select all locked way.
// sbu lock way from sbu input
wing_cbb_aomux #(.NUM(SBQ_ENT_N), .WIDTH(WAY_N)) u_aomux_sbqwl(.data_arr(sbu_ent_lock_way   ), .sel_in_onehot0(sbu_ent_lock_vld), .data_sel(sbu_locked_way));
// mb lock way from used way 
wing_cbb_aomux #(.NUM(MB_ENT_N ), .WIDTH(WAY_N)) u_aomux_mbwl (.data_arr(dff_mb_victim_way_q), .sel_in_onehot0(mb_ent_lock_vld ), .data_sel(mb_locked_way));

assign locked_way_m2  = sbu_locked_way | mb_locked_way;
assign all_way_locked = (MB_ENT_N < WAY_N) ? 1'b0 : 
                                            (&locked_way_m2 & cache_alloc_m2 & csr_dcache_en); //所有way都锁定 && 可分配 && 缓存使能


//=================================================================================
// Miss Buffer
//=================================================================================


// Following requests will enter missbuffer:
//  - stc   miss with write-allocate   (need_refill)
//  - load0 miss with read-allocate    (need_refill)
//  - load0 miss with no-read-allocate (regard as nc/device request, only need critial word but no allocation)
//  - load0 nc/device                  (always miss)

assign mb_push_en = need_refill                      //当前需要从主内存填充缓存行
                  | (ld0_real_vld_m2 & tag_is_miss)  //当有真实的加载请求且确实发生缓存缺失时，需要推送MB
                  | cbom_state_into_eb;              //缓存维护操作(Cache Maintenance Operation)状态进入驱逐缓冲区
//我们从最低有效位（右侧）开始计数，直到遇到第一个1为止
//原始输入 0001，取反后变成 1110, 从右往左数，最右边第一个是0，计数为1，接下来遇到1就停止   //例如0000: 0, 0001: 1, 0010: 0, 0011: 2
wing_cbb_lzc #(
    .WIDTH     ( MB_ENT_N ),   // 输入向量宽度 = 缺失缓冲区条目数(4)
    .DIRECTION ( 0 ),          //方向 = 0 (从右到左)
    .INVERT    ( 1 )           //反转 = 1 (反转输入向量)
) u_dcmb_lzc (
    .vec_i   ( dff_mb_vld_q ), //输入向量 = 缺失缓冲区有效位
    .cnt_o   ( mb_push_idx  ), //计数输出 = 缺失缓冲区推送索引
    .empty_o (              )
);

wing_cbb_bin2onehot #(.WIDTH(MB_ENT_N)) u_bin2oh_push (.bin_i(mb_push_idx), .onehot_o(mb_push_bitmap)); //way_index to way_onehot 

assign mb_full  =  &dff_mb_vld_q;
assign mb_empty = ~|dff_mb_vld_q;

// dff_mb_vld_clr condition:
//         1. linefill type entry 
//            * get arbiter linefill grant
//            * r_done and r_err both set (r_err does not participate in arbitration)
//         2. nc/device/ld_no-read-allocate type entry 
//            * get r-channel response
//         3. CBOM_clean eviction done.
// As for error response, non-refill type entry doesn't care error resposne.
// refill type entry is poped once it is selected from age_matrix.
// Flush no ar_sent and not selected being ar-sending entry is NOT included, because of critial timing.

assign mb_pop_lfl = agm_oldest_oh[AGM_LFL] & {MB_ENT_N{lfl_wr_rdy}}; //当Line Fill类型的条目是最老的且linefill完成时就把missbuf的数据pop出来

assign mb_pop_no_lfl = dff_mb_vld_q & ~dff_mb_is_refill_q & dff_mb_ar_sent_q & mb_ent_r_bitmap; //在有效的、非重填类型的、已发送地址请求中选择id的条目弹出

assign mb_pop_cbom = dff_mb_vld_q & dff_eb_done_q & {MB_ENT_N{cbom_state_clean}}; //当CBOM清理完成时弹出条目

// Bitmap of each missbuf entry r-channel handshake.
assign mb_ent_r_bitmap = bmu_dcu_r_sel & {MB_ENT_N{bmu_dcu_r_hdsk}}; //读握手missbuf id, 握手完成


// Miss_Buffer info, only include constant info once pushed into FIFO.
assign dff_mb_addr_d       = dff_addr_m2_q; //地址 to misbuf
assign dff_mb_size_d       = dff_size_m2_q; //大小 to misbuf
// Only idempotency, cacheable, load with read-allocate, store tag check with write-allocate requests need refill.
// However, store tag check with write-allocate is already filtered when pushed into mb, so only check dff_mb_tag_type_q.
assign dff_mb_is_refill_d  = csr_dcache_en &
                             ( dff_tag_type_m2_q[TAG_TYPE_STC]                                                                    //当前M2阶段的指令是存储指令（TAG_TYPE_STC）
                             | dff_tag_type_m2_q[TAG_TYPE_LD ] & real_idemp_m2 & real_ima_m2[MA_CACHE] & real_ima_m2[MA_RD_ALLOC] //当前M2阶段的指令是加载指令（TAG_TYPE_LD），并且该操作是幂等的，同时访问的内存区域是可缓存且支持读分配的
                             | dff_tag_type_m2_q[TAG_TYPE_PRF] );                                                                 //当前M2阶段的指令是预取指令（TAG_TYPE_PRF）

assign dff_mb_tag_type_d[TAG_TYPE_LD ] = dff_tag_type_m2_q[TAG_TYPE_LD ]; //加载 to misbuf
assign dff_mb_tag_type_d[TAG_TYPE_STC] = dff_tag_type_m2_q[TAG_TYPE_STC]; //存储 to misbuf
assign dff_mb_tag_type_d[TAG_TYPE_PRF] = dff_tag_type_m2_q[TAG_TYPE_PRF]; //预取 to misbuf  

assign dff_mb_idemp_d      = real_idemp_m2; //幂等 to misbuf
assign dff_mb_oma_d        = real_oma_m2; //主内存访问 to misbuf
assign dff_mb_excl_d       = dff_excl_m2_q; //独占 to misbuf
assign dff_mb_priv_d       = dff_priv_m2_q; //特权 to misbuf
assign dff_mb_master_d     = dff_master_m2_q; //主设备 to misbuf
assign dff_mb_victim_way_d = victim_way; //替换way to misbuf
assign dff_mb_extid_d      = dff_tag_id_m2_q; //id to misbuf


for (genvar i=0; i<MB_ENT_N; i++) begin : g_mb_info
  // {dff_mb_vld_set[i], dff_mb_vld_clr[i]} is onehot0 since we only find invalid entry to push,
  // and only pop valid enty.
  // dff_mb_vld_set is also onehot0 since there is only one push port for missbuf.
  // However, dff_mb_vld_clr is not onehot0 since multiple entry may be poped at the same time.
  assign dff_mb_vld_set[i] = mb_push_en & mb_push_bitmap[i]; //当推送使能 且 该入口被选中时，设置valid
  assign dff_mb_vld_clr[i] = mb_pop_lfl    [i] |             //当Line Fill类型的条目是最老的且写入就绪时弹出
                             mb_pop_no_lfl [i] |             //在有效的、非重填类型的、已发送地址请求中选择id的条目弹出
                             mb_pop_cbom   [i] ;             //当CBOM清理完成时弹出条目
  assign dff_mb_vld_en [i] = dff_mb_vld_set[i] | dff_mb_vld_clr[i]; //valid set || valid clear
  assign dff_mb_vld_d  [i] = dff_mb_vld_set[i]; //valid set

  `WDFFER(dff_mb_vld_q[i], dff_mb_vld_d[i], dff_mb_vld_en[i], clk, rst_n)

  assign dff_mb_info_en[i] = dff_mb_vld_set[i];
  // to missbuf regs
  `WDFFER(dff_mb_addr_q      [i], dff_mb_addr_d      , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_size_q      [i], dff_mb_size_d      , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_is_refill_q [i], dff_mb_is_refill_d , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_idemp_q     [i], dff_mb_idemp_d     , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_oma_q       [i], dff_mb_oma_d       , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_excl_q      [i], dff_mb_excl_d      , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_priv_q      [i], dff_mb_priv_d      , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_master_q    [i], dff_mb_master_d    , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_extid_q     [i], dff_mb_extid_d     , dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_victim_way_q[i], dff_mb_victim_way_d, dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_tag_type_q  [i], dff_mb_tag_type_d  , dff_mb_info_en[i], clk, rst_n)

  // Load flush only. No sbu flulsh since store instruction is post-commit.
  assign dff_mb_flush_set[i] = dff_mb_vld_q[i] & mb_ldsrc[i] & //missbuf有效 && 加载源有效 && 加载类型是load && 刷新有效 && 刷新id更年轻
                               dff_mb_tag_type_q[i][TAG_TYPE_LD] & lsu_dcu_flush_valid & 
                               id_younger(dff_mb_extid_q[i][LSQID_W-1:0], lsu_dcu_flush_lsqid);
  assign dff_mb_flush_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_flush_en [i] = dff_mb_flush_set[i] |  dff_mb_flush_clr[i];
  assign dff_mb_flush_d  [i] = dff_mb_flush_set[i] & ~dff_mb_flush_clr[i]; 

  `WDFFER(dff_mb_flush_q[i], dff_mb_flush_d[i], dff_mb_flush_en[i], clk, rst_n) //missbuf flush reg

// dff_mb_ar_sent_set[i] and dff_mb_ar_sent_clr[i] will not be 1 simultaneously, same reason as dff_mb_vld_set/clr.
  assign dff_mb_ar_sent_set[i] = agm_oldest_oh[AGM_AR][i] & dcu_bmu_ar_hdsk; //当AR通道是最老的且握手完成时，设置ar_sent
  assign dff_mb_ar_sent_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_ar_sent_en [i] = dff_mb_ar_sent_set[i] |  dff_mb_ar_sent_clr[i];
  assign dff_mb_ar_sent_d  [i] = dff_mb_ar_sent_set[i] & ~dff_mb_ar_sent_clr[i];
   
  `WDFFER(dff_mb_ar_sent_q[i], dff_mb_ar_sent_d[i], dff_mb_ar_sent_en[i], clk, rst_n) //missbuf ar_sent reg

  // each entry's response may be interleaved, hence, each entry has a independent r_counter.
  // When r_counter reached, set r_done, waiting linefill.
  // When ma is nc/device/no-alloate, release entry once response handshake.

  // Non-refill type entry r_cnt is always set to 0 since corresponding response is always 'last'.
  // Only refill type entry get non-last resposne, so r_counter plus one.
  assign dff_mb_r_cnt_en[i] = mb_ent_r_bitmap[i];
  assign dff_mb_r_cnt_d [i] = bmu_dcu_r_last ? {LINE_CNT_W{1'b0}} : (dff_mb_r_cnt_q[i] + 1'b1); //非重填类型entry r_cnt始终为0，因为对应的响应始终为'last'。只有重填类型entry需要非'last'响应，因此r_counter加1。

  `WDFFER(dff_mb_r_cnt_q[i], dff_mb_r_cnt_d[i], dff_mb_r_cnt_en[i], clk, rst_n)

  // When refill response reaches max number, set r_done and wait for cache linefill get arbiter grant.
  assign dff_mb_r_done_set[i] = mb_ent_r_bitmap[i] & bmu_dcu_r_last & (dff_mb_r_cnt_q[i] == AXI_LINE_LEN_N); //当r_counter达到最大值时，设置r_done，等待缓存行填充获得仲裁器授权。
  assign dff_mb_r_done_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_r_done_en [i] = dff_mb_r_done_set[i] | dff_mb_r_done_clr[i];
  assign dff_mb_r_done_d  [i] = dff_mb_r_done_set[i] & ~dff_mb_r_done_clr[i];

  `WDFFER(dff_mb_r_done_q[i], dff_mb_r_done_d[i], dff_mb_r_done_en[i], clk, rst_n)

  // Only refill type entry needs to record multiiple error response.
  // Non-refill type entry is directly poped once get resposne.
  assign dff_mb_r_err_set[i] = mb_ent_r_bitmap[i] & bmu_dcu_r_resp[1] & dff_mb_is_refill_q[i]; //选择miss_buf的id && 该id总线发生错误 && 且需要refill的
  assign dff_mb_r_err_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_r_err_en [i] = dff_mb_r_err_set[i] |  dff_mb_r_err_clr[i];
  assign dff_mb_r_err_d  [i] = dff_mb_r_err_set[i] & ~dff_mb_r_err_clr[i];

  `WDFFER(dff_mb_r_err_q[i], dff_mb_r_err_d[i], dff_mb_r_err_en[i], clk, rst_n)

  // Rotate the refill response data according to way and addr_offset(calculated from original offset plus r_cnt), 
  // then we just need to rotate the 4-bit data enable, instead of rotate the full 256-bit cacheline data.
  // After all r-channel beats, the full cacheline data will be rotated as corkscrew arrangement, 
  // which can be used for linefill directly. 
  // Reduce mux area and M1 arbiter timing.

  // The rotate offset binary is calculated from original address offset[4:3] plus r_cnt.
  // Because refill AR channel is based from critical word and wrap burst mode.
  // For example, victim_way = 1, original address offset (critical word offset) bit[4:3] = 2'b10.
  // Then the data distribution is:
  // 
  //   RAM    |  Bank Pair 3 |  Bank Pair 2 |  Bank Pair 1 |  Bank Pair 0 |
  // address  | Bank7  Bank6 | Bank5  Bank4 | Bank3  Bank2 | Bank1  Bank0 |
  //  [1:0]   |              |              |              |              |
  //          |              |              |              | way1   way1  |
  //  2'b11   |              |              |              | word7  word6 |
  //          |              |              |              |              |
  //          | way1   way1  |              |              |              |
  //  2'b10   | word5  word4 |              |              |              |
  //          |              |              |              |              |
  //          |              | way1   way1  |              |              |
  //  2'b01   |              | word3  word2 |              |              |
  //          |              |              |              |              |
  //          |              |              | way1   way1  |              |
  //  2'b00   |              |              | word1  word0 |              |
  //
  // R-Channel     beat 0         beat 3        beat 2         beat 1
  //

  // addr offset used to mux load response data from mbus rdata.
  assign mb_addr_ldoffs[i] = dff_mb_addr_q[i][2 +: LD_OFFS_W]; //取 [2 +: 1] = 地址的第2位 = 1'b0 或 1'b1, 用于选择64位数据中的高32位或低32位
 
  assign refill_rot_offs[i] = dff_mb_r_cnt_q[i] + dff_mb_addr_q[i][4 -: LINE_CNT_W]; // r通道接收计数器 + 行内的偏移（4：3）

  if (MBUS_W==64) begin : g_rfl_64

      assign refill_wr_data_en[i] = {WAY_N{mb_ent_r_bitmap[i] & dff_mb_is_refill_q[i]}} &  //只有需要回填的entry，才会使能写入；
                                  rotate_left_4bit(dff_mb_victim_way_q[i], refill_rot_offs[i]); //写入的way/bank位置会根据cache结构做旋转对齐，保证数据正确写入目标bank。
 
  end else if (MBUS_W==128) begin : g_rfl_128 //nouse

      // Two bank pairs will be written simultaneously when MBUS_W==128.

      logic [MB_ENT_N-1:0][1:0] refill_rot_offs_lo;
      logic [MB_ENT_N-1:0][1:0] refill_rot_offs_hi;

      assign refill_rot_offs_lo[i] = {refill_rot_offs[i], 1'b0};
      assign refill_rot_offs_hi[i] = {refill_rot_offs[i], 1'b1};

      assign refill_wr_data_en[i] = {WAY_N{mb_ent_r_bitmap[i] & dff_mb_is_refill_q[i]}} &
                                    ( rotate_left_4bit(dff_mb_victim_way_q[i], refill_rot_offs_lo[i])
                                    | rotate_left_4bit(dff_mb_victim_way_q[i], refill_rot_offs_hi[i]));

  end   // g_rfl_128

  // Both refill data and eviction data is stored in mb_data.
  // mb_data is divided into 4 banks, and already rotated in corkscrew arrangement:
  //   - For refill, each mbus response data is wrote into way rotated bank.  
  //   - For eviction, raw cacheline data is wrote into all 4 banks directly. 
  //     Select corresponding bank depend on way and offset/counter as w-channel data.
  //
  // dff_mb_data_en  |     [3]   |     [2]   |     [1]   |     [0]   |
  // dff_mb_data_d/q | [255:192] | [191:128] | [127: 64] | [63 :  0] |


  // Entry 3 (i=3)       Entry 2 (i=2)        Entry 1 (i=1)       Entry 0 (i=0)      
  // ┌─────────────────┐ ┌─────────────────┐  ┌─────────────────┐ ┌─────────────────┐
  //│ Way 3 (j=0)    │   │ Way 2 (j=0)    │  │ Way 1 (j=0)    │   │ Way 0 (j=0)    │ 
  //│ [63:0]         │   │ [63:0]         │  │ [63:0]         │   │ [63:0]  w67   │ 
  // ├─────────────────┤ ├─────────────────┤  ├─────────────────┤ ├─────────────────┤
  //│ Way 3 (j=1)    │   │ Way 2 (j=1)    │  │ Way 1 (j=1)    │   │ Way 0 (j=1)    │ 
  //│ [127:64]       │   │ [127:64]       │  │ [127:64]       │   │ [127:64]w45   │ 
  // ├─────────────────┤ ├─────────────────┤  ├─────────────────┤ ├─────────────────┤
  //│ Way 3 (j=2)    │   │ Way 2 (j=2)    │  │ Way 1 (j=2)    │   │ Way 0 (j=2)    │ 
  //│ [191:128]      │   │ [191:128]      │  │ [191:128]      │   │ [191:128]w23    │ 
  // ├─────────────────┤ ├─────────────────┤  ├─────────────────┤ ├─────────────────┤
  //│ Way 3 (j=3)    │   │ Way 2 (j=3)    │  │ Way 1 (j=3)    │   │ Way 0 (j=3)    │ 
  //│ [255:192]      │   │ [255:192]      │  │ [255:192]      │   │ [255:192]w10    │ 
  // └─────────────────┘ └─────────────────┘  └─────────────────┘ └─────────────────┘ 

  assign dff_mb_data_d[i] = evc_wr_mb_bitmap[i] ? evc_line_raw_data: //eviction
                                                  refill_line_data ; //refill
//j 表示 WAY_N 是因为在这个设计中，way的数量和bank的数量是相同的，每个way对应一个bank
  for (genvar j=0; j<WAY_N; j++) begin : g_mb_data  //way = bank
    assign dff_mb_data_en[i][j] = refill_wr_data_en[i][j]  //refill enablew
                                | evc_wr_mb_bitmap[i];    //wrback enablew

    `WDFFER(dff_mb_data_q[i][64*j+:64], dff_mb_data_d[i][64*j+:64], dff_mb_data_en[i][j], clk, rst_n) //missbuf data reg
  end 

  // Missbuf entries' set_index equal pipelien m2 set_index flag is used for miss-hit-miss and way-lock.
  assign mb_ent_eq_setidx_m2[i] = (dff_addr_m2_q[5+:SETIDX_W] == dff_mb_addr_q[i][5+:SETIDX_W]); //比较当前M2阶段请求的set index与miss buffer条目i的set index是否相等

  // M2 stage request hit miss_buffer entry.
  // Cacheline refill match cacheline size. 
  // However nc/device entry only match BMU size to reduce complexity.
  // And the reason to check nc/device MHM is that we need to keep load-load order for same address.
  assign hit_mb_ent_m2[i] = dff_mb_vld_q[i] & mb_ent_eq_setidx_m2[i] & //miss buffer条目i有效 && set index相等
                            (dff_addr_m2_q[31-:TAG_W] == dff_mb_addr_q[i][31-:TAG_W]) & //tag相等
                            ( dff_mb_is_refill_q[i] |  //重填类型m3
                              dff_mb_is_refill_d    |  //重填类型m2
                              (~dff_mb_is_refill_q[i] & ~dff_mb_is_refill_d &  //m2，m3非重填类型
                              (dff_addr_m2_q[4:MBUS_SIZE_N] == dff_mb_addr_q[i][4:MBUS_SIZE_N])) ); //地址大小相等

  // Missbufer entry lock
  assign mb_ent_lock_vld[i] = dff_mb_vld_q[i] & mb_ent_eq_setidx_m2[i] & dff_mb_is_refill_q[i]; //miss buffer条目i有效 && set index相等 && 重填类型

end // g_mb_info


if (MBUS_W==64) begin : g_rfl_data64
 
    assign refill_line_data = {LINE_LEN_N{bmu_dcu_r_data}}; //refill data is axi_r_data

end else if (MBUS_W==128) begin : g_rfl_data128 //nouse

  wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N)) u_aomux_rfl_way (.data_arr(dff_mb_victim_way_q), .sel_in_onehot0(mb_ent_r_bitmap), .data_sel(rfl_victim_way));

  // way 0 data start at bank pair 0
  // way 1 data start at bank pair 1
  // way 2 data start at bank pair 2
  // way 3 data start at bank pair 3
  // Hence, when victim way inside {0, 2}, use original duplicated r_data;
  // when victim way inside {1, 3}, use one bank-pair rotated r_data.

  assign refill_line_data = (rfl_victim_way[1] | rfl_victim_way[3])
                              ? {LINE_LEN_N{bmu_dcu_r_data[63:0], bmu_dcu_r_data[127:64]}}
                              : {LINE_LEN_N{bmu_dcu_r_data}} ;
 
end // g_rfl_data128


// M2 request hit miss_buffer.
assign hit_mb_m2 = |hit_mb_ent_m2;

// Because hit_mb_m2 and dff_mb_vld_clr may be in the same cycle. ??todo: 为什么？
// However, we can't use dff d pin as hit_gen logic. 
// So pipeline dff_mb_vld_clr as wakeup signal.
assign dff_mb_rls_array_en = (|dff_mb_vld_clr) | (|dff_mb_rls_array_q); //当前周期有清除信号 | 上一周期有未处理的清除信号
assign dff_mb_rls_array_d  = dff_mb_vld_clr;

`WDFFER(dff_mb_rls_array_q, dff_mb_rls_array_d, dff_mb_rls_array_en, clk, rst_n)


// Miss_Buffer is based on age matrix.
// There are 4 mask vector to get each oldest entry at the same time.
//  - oldest entry for AR channel, refill and nc/device load.
//  - oldest entry for linefill request to DCache arbiter.
//  - oldest entry for eviction read request to DCache arbiter.
//  - oldest entry for AW channel, cacheline evict.
//  - oldest entry for W  channel, cacheline evict data.

// Refill request will be sent to BMU if no eviction needed(no evict or vimctim cacheline clean) 
// or eviction data is already sent.
assign agm_query_vld_mask[AGM_AR ] = dff_mb_vld_q & ~dff_mb_ar_sent_q & {MB_ENT_N{~cbom_state_clean}} &  //miss_buf data vld && ~ar_sent && no cbom_clean ins
                                     (~dff_eb_vld_q    //没有evict buffer请求（不需要驱逐）
                                     | dff_eb_done_q   //evict buffer已完成（驱逐完成）
                                     | dff_eb_aw_sent_q & dff_eb_w_sent_q); //evict buffer的写请求已发送（虽然还没完成，但不会阻塞AR）
 //缺失缓冲区有效 && 仲裁请求已发送 && 内存读取操作完成，数据准备就绪 && 确保行填充不会与正在进行的逐出冲突
assign agm_query_vld_mask[AGM_LFL] = dff_mb_vld_q & dff_mb_ar_sent_q & dff_mb_r_done_q & (~dff_eb_vld_q | dff_eb_done_q); //mb已经分配，同时已经从总线load回数据 || evict buffer无效或已完成

assign agm_query_vld_mask[AGM_EVC] = dff_mb_vld_q & dff_eb_vld_q & ~dff_eb_done_q & ~dff_eb_evc_sent_q; //miss_entry有效 && evict buffer有效 && evict未完成 && evict请求未发送

assign agm_query_vld_mask[AGM_AW ] = dff_mb_vld_q & dff_eb_vld_q & ~dff_eb_done_q & ~dff_eb_aw_sent_q;  //miss_entry有效 && evict buffer有效 && evict未完成 && evict aw请求未发送

assign agm_query_vld_mask[AGM_W  ] = dff_mb_vld_q & dff_eb_vld_q & ~dff_eb_done_q & ~dff_eb_w_sent_q & dff_eb_data_rdy_q; // miss_entry有效 && evict buffer有效 && evict未完成 && evict aw请求未发送 && evict aw请求未发送


wing_cbb_age_matrix #(
  .ENTRY_NUM       ( MB_ENT_N ), // 条目数，等于miss buffer的entry数量
  .WR_NUM          ( 1        ), // 写端口数为1
  .FIND_OLDEST     ( 1        ), // 使能查找最老条目
  .RD_NUM_OLD      ( 1        ), // 最老条目读端口数为1
  .FIND_NEWEST     ( 0        ), // 不查找最新条目
  .RD_NUM_NEW      ( 1        ), // 最新条目读端口数为1
  .VLD_VEC_NUM_OLD ( 5        ), // 最老条目有效性向量数为5
  .VLD_VEC_NUM_NEW ( 1        )  // 最老条目有效性向量数1
) u_dcmb_age_matrix (
    .clk            ( clk                ),
    .rst_n          ( rst_n              ),
    .wr_en          ( mb_push_en         ), // 写使能，miss buffer有新条目入队时为高
    .wr_id          ( mb_push_idx        ), // 写入索引，指示新条目写入哪个位置
    .oldest_rd_oh   ( agm_oldest_oh      ), // 输出，最老条目的onehot编码
    .newest_rd_oh   (                    ),
    .oldest_vld_vec ( agm_query_vld_mask ),
    .newest_vld_vec ( {MB_ENT_N{1'b0}}   )
);


//=================================================================================
// Evict Buffer
//=================================================================================

// Evict process
// 1. Set eb_vld once need to（no eb_vld）evict a cacheline, record evict tag.
// 2. Select oldest entry in (eb_vld & ~aw_sent) entries to send aw-channel.
//      - set aw_sent once aw-channel handshake.
// 3. Select oldest entry in (eb_vld & ~eb_evc_sent) entries to send evc_read req to M1 arbiter.
//      - set eb_evc_sent once arbiter granted.
//      - set eb_data_rdy when evc_response return.
// 4. Select oldest entry in (eb_vld & eb_data_rdy & ~w_sent) entries to send w-channel.
//      - set w_sent once last data w-channel handshake.
// 5. Set eb_done when B response handshake.
//    (Report RAS if response error)

// Miss_buffer and evict_buffer entries are one-to-one bound together.
// Hence, eb_push_en depend on miss_buffer not full.
// However, load cancel, flush logic and replay etc. is already included in need_refill.

// As long as victim_way is valid, the victim cacheline should be pushed into evict buffer, 
// but does not enter real eviction process. This case is just used as a placeholder, 
// then later same address ld/st would hit evict_buffer and convert to miss.
// The reason is that valid bit in tag RAM is not cleared for evicted cacheline, 
// so we need this mechanism to avoid this 'fake' hit.

assign eb_push_en  = need_refill & victim_way_valid_bit //需要填充且victim way有效
                   | cbom_state_into_eb; //cbom状态进入eb

assign dff_eb_tag_d  = victim_way_tag_bits; //替换way的tag
assign dff_eb_priv_d = |core_priv_mode;

for (genvar i=0; i<MB_ENT_N; i++) begin : g_eb

  assign dff_eb_vld_set[i] = eb_push_en & mb_push_bitmap[i]; //使能且该条目被选中
  assign dff_eb_vld_clr[i] = dff_mb_vld_clr[i] ;//与miss buffer同步清除
  assign dff_eb_vld_en [i] = dff_eb_vld_set[i] |  dff_eb_vld_clr[i];
  assign dff_eb_vld_d  [i] = dff_eb_vld_set[i] & ~dff_eb_vld_clr[i];

  `WDFFER(dff_eb_vld_q[i], dff_eb_vld_d[i], dff_eb_vld_en[i], clk, rst_n) //第一阶段，evict_buf valid

  assign dff_eb_tag_en[i] = dff_eb_vld_set[i]; //missbuf有效位时更新tag

  `WDFFER(dff_eb_tag_q[i], dff_eb_tag_d, dff_eb_tag_en[i], clk, rst_n)   //eb tag reg

  assign dff_eb_priv_en[i] = dff_eb_vld_set[i]; //missbuf有效位时更新privilege

  `WDFFER(dff_eb_priv_q[i], dff_eb_priv_d, dff_eb_priv_en[i], clk, rst_n) //eb priv reg

  // Eviction read cacheline request already sent (handshake) to M1 arbiter.
  assign dff_eb_evc_sent_set[i] = agm_oldest_oh[AGM_EVC][i] & evc_rd_rdy;    //第二阶段，最老条目且evict仲裁完成
  assign dff_eb_evc_sent_clr[i] = dff_mb_vld_clr[i];
  assign dff_eb_evc_sent_en [i] = dff_eb_evc_sent_set[i] |  dff_eb_evc_sent_clr[i];
  assign dff_eb_evc_sent_d  [i] = dff_eb_evc_sent_set[i] & ~dff_eb_evc_sent_clr[i];

  `WDFFER(dff_eb_evc_sent_q[i], dff_eb_evc_sent_d[i], dff_eb_evc_sent_en[i], clk, rst_n) 

  // Eviction read cacheline data already registered in mb_data.
  assign dff_eb_data_rdy_set[i] = evc_wr_mb_bitmap[i];
  assign dff_eb_data_rdy_clr[i] = dff_mb_vld_clr  [i];
  assign dff_eb_data_rdy_en [i] = dff_eb_data_rdy_set[i] |  dff_eb_data_rdy_clr[i];
  assign dff_eb_data_rdy_d  [i] = dff_eb_data_rdy_set[i] & ~dff_eb_data_rdy_clr[i];

  `WDFFER(dff_eb_data_rdy_q[i], dff_eb_data_rdy_d[i], dff_eb_data_rdy_en[i], clk, rst_n) //第三阶段，驱逐数据写入miss buffer

  // AW-channel already handshake.
  assign dff_eb_aw_sent_set[i] = agm_oldest_oh[AGM_AW][i] & dcu_bmu_aw_hdsk;
  assign dff_eb_aw_sent_clr[i] = dff_mb_vld_clr[i];
  assign dff_eb_aw_sent_en [i] = dff_eb_aw_sent_set[i] |  dff_eb_aw_sent_clr[i];
  assign dff_eb_aw_sent_d  [i] = dff_eb_aw_sent_set[i] & ~dff_eb_aw_sent_clr[i];

  `WDFFER(dff_eb_aw_sent_q[i], dff_eb_aw_sent_d[i], dff_eb_aw_sent_en[i], clk, rst_n) //第四阶段，最老条目且AW握手完成

  // W-channel already completed (4-beat handshake for 64-bit mbus width).
  assign dff_eb_w_sent_set[i] = agm_oldest_oh[AGM_W][i] & dcu_bmu_w_hdsk & dcu_bmu_w_last;
  assign dff_eb_w_sent_clr[i] = dff_mb_vld_clr[i];
  assign dff_eb_w_sent_en [i] = dff_eb_w_sent_set[i] |  dff_eb_w_sent_clr[i];
  assign dff_eb_w_sent_d  [i] = dff_eb_w_sent_set[i] & ~dff_eb_w_sent_clr[i];
  
  `WDFFER(dff_eb_w_sent_q[i], dff_eb_w_sent_d[i], dff_eb_w_sent_en[i], clk, rst_n) //第五阶段，W通道请求已发送

  // - for normal dirty victim cacheline, set eb_done when b-channel handshake.
  // - for clean victim cacheline, set eb_done when pushed into eb, so does not do real eviction.
  assign dff_eb_done_set[i] = eb_ent_b_bitmap[i] | 
                              dff_eb_vld_set[i] & ~victim_way_dirty_bit;
  assign dff_eb_done_clr[i] = dff_mb_vld_clr[i];
  assign dff_eb_done_en [i] = dff_eb_done_set[i] |  dff_eb_done_clr[i];
  assign dff_eb_done_d  [i] = dff_eb_done_set[i] & ~dff_eb_done_clr[i];

  `WDFFER(dff_eb_done_q[i], dff_eb_done_d[i], dff_eb_done_en[i], clk, rst_n) //第六阶段，B通道握手完成

  // eb entry address, catenated with eb entry tag and mb entry address index field.
  // Because eviction cacheline and refill cacheline is in same cache-set. Reuse this set index field to save area.
  assign eb_setidx[i] = dff_mb_addr_q[i][5+:SETIDX_W];          //eb set index 与missbuf相同
  assign eb_addr  [i] = {dff_eb_tag_q[i], eb_setidx[i], 5'b0};  //发送给总线的写地址

  // Pipeline m2 request hit evict_buffer entry.
  assign hit_eb_ent_m2      [i] = dff_eb_vld_q[i] & (dff_addr_m2_q[31:5] == eb_addr[i][31:5]); //M2阶段地址与evict buffer地址匹配
  assign hit_eb_ent_ndone_m2[i] = hit_eb_ent_m2[i] & ~dff_eb_done_q[i]; //M2阶段地址与evict buffer地址匹配且驱逐未完成
  assign hit_eb_ent_done_m2 [i] = hit_eb_ent_m2[i] &  dff_eb_done_q[i]; //M2阶段地址与evict buffer地址匹配且驱逐完成
 
  assign ld1_hit_eb_ent_done_m2[i] = dff_eb_vld_q[i] & dff_eb_done_q[i] &  //Evict Buffer条目i有效 && 驱逐过程已完成 && ld1地址匹配
                                    (dff_ld1_addr_m2_q[31:5] == eb_addr[i][31:5]); 

end // g_eb

// Convert b-channel id to onehot as eb-done enable.
wing_cbb_bin2onehot #(.WIDTH(MB_ENT_N)) u_bin2oh_bid (.bin_i(bmu_dcu_b_id[MB_ID_W-1:0]), .onehot_o(bmu_dcu_b_sel)); //将B通道的二进制ID转换为独热编码

assign eb_ent_b_bitmap = bmu_dcu_b_sel & {MB_ENT_N{bmu_dcu_b_hdsk}}; //标识哪个Evict Buffer条目完成了B通道握手
 
assign eb_b_err = bmu_dcu_b_vld & bmu_dcu_b_resp[1]; //B通道握手完成且总线错误

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(32)) u_aomux_b_addr (.data_arr(eb_addr), .sel_in_onehot0(eb_ent_b_bitmap), .data_sel(eb_b_addr)); //获取完成写操作的Evict Buffer条目的地址


// Hit evict buffer status and flag lookup table.
//
//              | IDLE  | waiting-evict | evicting      | waiting-b | evict done |
// eb_vld       | 0     | 1             | 1             | 1         | 1          |
// evc_sent     | 0     | 1             | 1             | 1         | 1          |
// eb_data_rdy  | 0     | 1             | 1             | 1         | 1          |
// aw_sent      | 0     | 0 & ~selected | 1 or selected | 1         | 1          |
// w_sent       | 0     | 0             | 0             | 1         | 1          |
// eb_done      | 0     | 0             | 0             | 0         | 1          |
 
// Handling hit evict buffer
// The reason of replay waiting-b case is to keep order for AXI same address request,
// because the younger refill request could be completed earlier, before the evcition b-response is back.
// 
//               | waiting-evict or evicting or waiting-b | evict done         |
// ld[0]         | ignore (hit)                           | regard as miss     |
// ld[1] samecl  | ignore (hit)                           | MHM replay         |
// ld[1] utag    | ignore (hit)                           | auto wakeup replay |
// store tag chk | HEB replay                             | regard as miss     |

assign hit_eb_m2           = |hit_eb_ent_m2;   //任何M2请求命中Evict Buffer
assign hit_eb_as_replay_m2 = |hit_eb_ent_ndone_m2; //命中但驱逐未完成，需要重放
assign hit_eb_as_miss_m2   = |hit_eb_ent_done_m2 ; //命中且驱逐已完成，视为缺失
assign ld1_hit_eb_done_m2  = |ld1_hit_eb_ent_done_m2; //ld1请求命中且驱逐已完成

// Because hit_eb_m2 and eb_ent_b_bitmap may be in the same cycle.
// However, we can't use dff d pin as hit_gen logic. 
// So pipeline eb_ent_b_bitmap as wakeup signal.
assign dff_eb_rls_array_en = (|eb_ent_b_bitmap) | (|dff_eb_rls_array_q);
assign dff_eb_rls_array_d  = eb_ent_b_bitmap;

`WDFFER(dff_eb_rls_array_q, dff_eb_rls_array_d, dff_eb_rls_array_en, clk, rst_n)


//=================================================================================
// Full line (linefill/eviction) Pre-Process 
//=================================================================================

// eviction read request to DCache arbiter
assign evc_rd_vld = |agm_query_vld_mask[AGM_EVC];

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(SETIDX_W)) u_aomux_evc_set (.data_arr(eb_setidx          ), .sel_in_onehot0(agm_oldest_oh[AGM_EVC]), .data_sel(evc_setidx));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N   )) u_aomux_evc_way (.data_arr(dff_mb_victim_way_q), .sel_in_onehot0(agm_oldest_oh[AGM_EVC]), .data_sel(evc_way   ));

wing_cbb_onehot2bin #(.WIDTH(MB_ENT_N)) u_oh2bin_evc (.onehot_i(agm_oldest_oh[AGM_EVC]), .bin_o(evc_id));

// Eviction request read a full cacheline in parallel. 驱逐请求会并行读取一个完整的缓存行（256位）
// Although the cache RAMs are arranged in corkscrew, the eviction data is NOT rotated before sent to BMU.驱逐数据在发送给BMU之前不进行旋转，直接使用原始读取的256位数据
// When sending w-channel to BMU, 64-bit data is muxed from 256-bit registered line data every beat.向BMU发送W通道数据时，每个时钟周期选择64位数据，从256位寄存器数据中通过多路复用器选择
// So we just need to rotate the mux selection input according to the way,只旋转多路复用器的选择信号，而不是旋转整个缓存行数据
// instead of rotating the full cacheline data, to save the mux area.这样可以节省多路复用器的面积
assign evc_line_raw_data = {ram_data1_rdata[3][31:0],  //ram_data1_rdata[3][31:0]表示缓存行中第3个64位数据
                            ram_data0_rdata[3][31:0],  //ram_data0_rdata[3][31:0]表示缓存行中第3个64位数据
                            ram_data1_rdata[2][31:0],  //...
                            ram_data0_rdata[2][31:0],
                            ram_data1_rdata[1][31:0], 
                            ram_data0_rdata[1][31:0],
                            ram_data1_rdata[0][31:0], 
                            ram_data0_rdata[0][31:0] };

wing_cbb_bin2onehot #(.WIDTH(MB_ENT_N)) u_bin2oh_evcwr (.bin_i(dff_dat_id_m2_q[MB_ID_W-1:0]), .onehot_o(evc_wr_sel)); //将M2阶段的ID转换为独热编码
 
assign evc_wr_mb_bitmap = evc_wr_sel & {MB_ENT_N{dff_dat_type_m2_q[DAT_TYPE_EVC]}}; //标识哪个Miss Buffer条目需要接收驱逐数据


// Linefill write request to DCache arbiter
assign lfl_wr_vld = |agm_query_vld_mask[AGM_LFL];

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N)) u_aomux_lfl_way (.data_arr(dff_mb_victim_way_q), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_way ));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(32   )) u_aomux_lfl_addr(.data_arr(dff_mb_addr_q      ), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_addr));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1    )) u_aomux_lfl_err (.data_arr(dff_mb_r_err_q     ), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_err ));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(256  )) u_aomux_lfl_data(.data_arr(dff_mb_data_q      ), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_data));
// Type and ID are used for dbu_sbu_filldone_sbqid.
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(3    )) u_aomux_lfl_type(.data_arr(dff_mb_tag_type_q  ), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_tag_type));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(ID_EXT_W)) u_aomux_lfl_extid(.data_arr(dff_mb_extid_q   ), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_extid   ));

assign lfl_tag_bank_cs = lfl_way;//需要refill时，选择miss_buf中victim的way进行refill

assign lfl_tag_wdata[TAG_DIRTY_BIT] = 1'b0;  //refill后dirty一定为0
// If error occured in refill data response process, linefill write invalid instead,
// to ensure replaced way is cleared.
assign lfl_tag_wdata[TAG_VALID_BIT] = ~lfl_err;  //refill没有错误时valid为1，否则为0
assign lfl_tag_wdata[TAG_W-1:0]     = lfl_addr[31-:TAG_W]; //refill的tag为miss_buf中victim的addr

// Same error handling as lfl_tag_bank_cs.
assign lfl_dat_bank_cs = {WAY_N{~lfl_err}}; //refill没有错误时，选择miss_buf中victim的way进行refill, 假设way0为victim，way0的数据遍布在pair0，pair1，pair2，pair3中，所以lfl_dat_bank_cs为1111
 
// lfl_dat_ebank_wdata is in ECC section.

// Determine the way being accessed for linefill and eviction_read requests
assign line_data_way = ({4{pab_dat_grt[PAB_DAT_LFL]}} & lfl_way) //linefill时，选择miss_buf中victim的way进行refill
                     | ({4{pab_dat_grt[PAB_DAT_STD]}} & sbu_dcu_data_req_wayoh) //store时，选择sbu_dcu_data_req_wayoh
                     | ({4{pab_dat_grt[PAB_DAT_EVC]}} & evc_way) ; //eviction时，选择evict_buf中victim的way进行eviction

// For linefill and eviction_read requests which access a full cacheline as 4-sequential 64-bit accesses,
// determine the data address offset in each bank-pair according to the line_data_way.
// Full line-accesses are always aligned to a 32-byte address.


always_comb begin : g_line_addr_offs
  unique case (line_data_way)
  4'b0001: {line_data_addr67, line_data_addr45, line_data_addr23, line_data_addr01} = {2'b11, 2'b10, 2'b01, 2'b00}; //分别访问bank pair3的11，分别访问bank pair2的10，分别访问bank pair1的01，分别访问bank pair0的00
  4'b0010: {line_data_addr67, line_data_addr45, line_data_addr23, line_data_addr01} = {2'b10, 2'b01, 2'b00, 2'b11}; //分别访问bank pair3的10，分别访问bank pair2的01，分别访问bank pair1的00，分别访问bank pair0的11
  4'b0100: {line_data_addr67, line_data_addr45, line_data_addr23, line_data_addr01} = {2'b01, 2'b00, 2'b11, 2'b10}; //分别访问bank pair3的01，分别访问bank pair2的00，分别访问bank pair1的11，分别访问bank pair0的10
  4'b1000: {line_data_addr67, line_data_addr45, line_data_addr23, line_data_addr01} = {2'b00, 2'b11, 2'b10, 2'b01}; //分别访问bank pair3的00，分别访问bank pair2的11，分别访问bank pair1的10，分别访问bank pair0的01
  default: {line_data_addr67, line_data_addr45, line_data_addr23, line_data_addr01} = {2'bxx, 2'bxx, 2'bxx, 2'bxx}; 
endcase
end


//=================================================================================
// Micro-Tag
//=================================================================================

// Micro-tag entry number, each way has UTAG_ENT_N entries.
// Available ranges: 0, 1, 2, 4, 8, 16, 32.
// 4路组相连，每个way有2个entry
localparam UTAG_ENT_N = 2;                       //每个way的micro-tag entry数量（可配置）， 
localparam UTAG_IDX_W = $clog2(UTAG_ENT_N);      //每个way的micro-tag index数量（可配置）。 1位即可表示2个entry
// Micro-tag entry tag width. 
// Micro-tag address tag are longer than main address tag
// since micro-tag is subset of main tag.
localparam UTAG_TAG_W = 32-5-UTAG_IDX_W;         //micro-tag的tag宽度，通常比主tag更长。tag=32-5-1=26位， index=1位
localparam UTAG_EN    = (UTAG_ENT_N != 0);       //是否启用micro-tag。启动状态

if (UTAG_EN) begin : g_utag

  logic [WAY_N-1:0]                   utag_hit_updt_way_m2; //命中且需要更新PLRU的way
  logic [UTAG_ENT_N-1:0]              utag_hit_updt_ent_m2; //命中且需要更新PLRU的entry
  logic [WAY_N-1:0][UTAG_ENT_N-1:0]   utag_hit_updt_en_m2 ; //命中且需要更新PLRU的entry的使能信号
  logic [UTAG_TAG_W-1:0]              utag_hit_tag_m2     ; //命中且需要更新PLRU的tag
  logic [WAY_N-1:0]                   utag_lfl_updt_way_m1; //linefill时需要更新的way
  logic [UTAG_ENT_N-1:0]              utag_lfl_updt_ent_m1; //linefill时需要更新的entry
  logic [WAY_N-1:0][UTAG_ENT_N-1:0]   utag_lfl_updt_en_m1 ; //linefill时需要更新的entry的使能信号
  logic [UTAG_TAG_W-1:0]              utag_lfl_tag_m1     ; //linefill时需要更新的tag

  logic                               invld_inv_utag; //invalidate all时需要更新的entry
  logic                               cmo_inv_utag; //cbo invalidate操作时需要更新的entry
  logic                               lfl_inv_utag; //linefill bus error时需要更新的entry
  logic [WAY_N-1:0]                   utag_inv_updt_way_m1; //失效时需要更新的way
  logic [UTAG_ENT_N-1:0]              utag_inv_updt_ent_m1; //失效时需要更新的entry
  logic [WAY_N-1:0][UTAG_ENT_N-1:0]   utag_inv_updt_en_m1 ; //失效时需要更新的entry的使能信号

  assign utag_hit_updt_way_m2 = tag_way_cmp & {WAY_N{hit_update_plru}};           //判断ld0命中的哪一个way需要更新utag（ld0命中且需要更新PLRU）。
  assign utag_hit_tag_m2 = dff_addr_m2_q[31-:UTAG_TAG_W];                         //当前需要更新的utag的tag值。来源于ld0的地址

  assign utag_lfl_updt_way_m1 = lfl_tag_bank_cs & {WAY_N{lfl_wr_rdy & ~lfl_err}}; //linefill时需要更新data_pair的index。来源于miss_buf中victim的way
  assign utag_lfl_tag_m1 = lfl_addr[31-:UTAG_TAG_W];                              //linefill时需要写入的tag。来源于miss_buf中的地址

  // utag is smaller than main-tag, so only a small part of counter are updated for utag.
  assign invld_inv_utag = invld_arb_req & (&dff_invld_cnt_q[SETIDX_W-1:UTAG_IDX_W]) & (&dff_invld_cnt_offs_q); //invalidate all时，部分counter变化需要更新utag。
  assign cmo_inv_utag   = cbom_state_inval & ~cbom_tag_wdata[TAG_VALID_BIT];                                   //cbo invalidate操作时需要失效utag。
  // When linefill bus error occurred, coressponding cacheline should be invalidated. So as utag.
  assign lfl_inv_utag   = lfl_wr_rdy & lfl_err;                                                               //linefill bus error时需要失效utag。

  assign utag_inv_updt_way_m1 = {WAY_N{invld_inv_utag}} //{WAY_N{invld_inv_utag}} 表示所有4个Way都需要失效
                              | {WAY_N{cmo_inv_utag  }} & dff_cbom_way_q // 只失效CMO操作指定的Way
                              | {WAY_N{lfl_inv_utag  }} & lfl_tag_bank_cs; //当Linefill操作发生总线错误时，需要失效对应的缓存行

  for (genvar i=0; i<UTAG_ENT_N; i++) begin : g_utag_updt_ent  //针对每个entry，判断是否需要被命中/linefill/失效操作更新。

    assign utag_hit_updt_ent_m2[i] = (dff_addr_m2_q[5+:UTAG_IDX_W] == i); //ld0命中时，判断ld0的地址是否与当前entry的index匹配
    assign utag_lfl_updt_ent_m1[i] = (lfl_addr[5+:UTAG_IDX_W] == i); //linefill时，判断linefill的地址是否与当前entry的index匹配
    assign utag_inv_updt_ent_m1[i] = invld_inv_utag & (dff_invld_cnt_q[UTAG_IDX_W-1:0] == i) //invalidate all时，判断counter的index是否与当前entry的index匹配
                                   | cmo_inv_utag   & (cbom_tag_setidx[UTAG_IDX_W-1:0] == i) //cbo invalidate操作时，判断counter的index是否与当前entry的index匹配
                                   | lfl_inv_utag   & (lfl_addr[5+:UTAG_IDX_W] == i); //当Linefill操作发生总线错误时，需要失效对应的缓存行
 
  end

  for (genvar i_way=0; i_way<WAY_N; i_way++) begin : g_utag_way //针对每个way，生成每个entry的更新/失效使能信号。
 
    assign utag_hit_updt_en_m2[i_way] = {UTAG_ENT_N{utag_hit_updt_way_m2[i_way]}} & utag_hit_updt_ent_m2; //ld0命中了way，同时ld0的index与当前entry的index匹配，则更新utag 对应的way
    assign utag_lfl_updt_en_m1[i_way] = {UTAG_ENT_N{utag_lfl_updt_way_m1[i_way]}} & utag_lfl_updt_ent_m1; //ld0 refill了哪个way，同时refill的index与当前entry的index匹配，则更新utag对应的way
    assign utag_inv_updt_en_m1[i_way] = {UTAG_ENT_N{utag_inv_updt_way_m1[i_way]}} & utag_inv_updt_ent_m1; //失效时，判断失效的地址是否与当前entry的index匹配，则更新对应的way

    // Attention! utag is only used for lsu ld request now.
    // vpu ld request doesn't check utag to improve timing.
    chunjun_dcu_utag_way #(  // 最后实例化micro-tag模块，传入所有相关信号。
      .UTAG_ENT_N (UTAG_ENT_N ),
      .UTAG_TAG_W (UTAG_TAG_W )
    ) u_utag_way (
      .clk         ( clk                             ),
      .rst_n       ( rst_n                           ),
      .ld1_vld_m1  ( lsu_dcu_ld_req_valid_m1[1]      ),
      .ld1_addr_m1 ( lsu_dcu_ld_req_addr_m1[1][31:5] ),
      .ld1_hit_m1  ( utag_ld1_hit_way_m1[i_way]      ),
      .hit_updt_m2 ( utag_hit_updt_en_m2[i_way]      ),      
      .hit_tag_m2  ( utag_hit_tag_m2                 ),
      .lfl_updt_m1 ( utag_lfl_updt_en_m1[i_way]      ),
      .lfl_tag_m1  ( utag_lfl_tag_m1                 ),
      .inv_updt_m1 ( utag_inv_updt_en_m1[i_way]      )
    );

  end // g_utag_way

  assign utag_ld1_bank_pair_m1 = rotate_left_4bit(utag_ld1_hit_way_m1, ld_req_addr_m1[1][4:3]);  //将ld1的index旋转到正确的位置
 
  // opt -> avoid utag way to be evicted.

end else begin : g_no_utag //如果未启用micro-tag，则将所有信号设置为0
 
  assign utag_ld1_hit_way_m1 = {WAY_N{1'b0}}; 
 
  assign utag_ld1_bank_pair_m1 = {WAY_N{1'b0}};
 
end // UTAG_EN


//=================================================================================
// ECC
//=================================================================================

//**********************************************************************************************//
//**********************************************************************************************//
`ifdef CHUNJUN_SUPPORT_MEM_ECC
//**********************************************************************************************//
//**********************************************************************************************//

logic [WAY_N-1:0][38:0]                 ram_tag_rdata_pad_m2;
logic [WAY_N-1:0][38:0]                 ram_tag_post_dec_m2;
logic [WAY_N-1:0]                       ram_tag_err_m2;
logic [WAY_N-1:0]                       ram_tag_sec_m2;
logic [WAY_N-1:0]                       ram_tag_ded_m2;

logic                                   tag_ecc_sec_m3;
logic                                   tag_ecc_ded_m3;
logic                                   dff_ram_tag_sec_m3_en ;
logic [WAY_N-1:0]                       dff_ram_tag_sec_m3_d  ;
logic [WAY_N-1:0]                       dff_ram_tag_sec_m3_q  ;
logic                                   dff_ram_tag_ded_m3_en ;
logic [WAY_N-1:0]                       dff_ram_tag_ded_m3_d  ;
logic [WAY_N-1:0]                       dff_ram_tag_ded_m3_q  ;

logic                                   dff_ram_tag_post_dec_m3_en;
logic [WAY_N-1:0][TAG_RAM_D_W-1:0]      dff_ram_tag_post_dec_m3_d ;
logic [WAY_N-1:0][TAG_RAM_D_W-1:0]      dff_ram_tag_post_dec_m3_q ;

logic                                   dff_tag_setidx_m3_en;
logic [SETIDX_W-1:0]                    dff_tag_setidx_m3_d ;
logic [SETIDX_W-1:0]                    dff_tag_setidx_m3_q ;

logic                                   dff_ebank_touched_m2_set;
logic                                   dff_ebank_touched_m2_clr;
logic                                   dff_ebank_touched_m2_en ;
logic                                   dff_ebank_touched_m2_d  ;
logic                                   dff_ebank_touched_m2_q  ;
logic                                   dff_obank_touched_m2_set;
logic                                   dff_obank_touched_m2_clr;
logic                                   dff_obank_touched_m2_en ;
logic                                   dff_obank_touched_m2_d  ;
logic                                   dff_obank_touched_m2_q  ;

logic                                   dff_std_addr_m2_en;
logic [31:0]                            dff_std_addr_m2_d ;
logic [31:0]                            dff_std_addr_m2_q ;
logic [31:0]                            ebank_addr_m2;
logic [31:0]                            obank_addr_m2;
logic                                   dff_ebank_addr_m3_en;
logic [31:0]                            dff_ebank_addr_m3_d ;
logic [31:0]                            dff_ebank_addr_m3_q ;
logic                                   dff_obank_addr_m3_en;
logic [31:0]                            dff_obank_addr_m3_d ;
logic [31:0]                            dff_obank_addr_m3_q ;
logic                                   dat_ebank_pair_m3_en;
logic [WAY_N-1:0]                       dat_ebank_pair_m3_d ;
logic [WAY_N-1:0]                       dat_ebank_pair_m3_q ;
logic                                   dat_obank_pair_m3_en;
logic [WAY_N-1:0]                       dat_obank_pair_m3_d ;
logic [WAY_N-1:0]                       dat_obank_pair_m3_q ;
logic                                   dat_ebank_code_m3_en;
logic [6:0]                             dat_ebank_code_m3_d ;
logic [6:0]                             dat_ebank_code_m3_q ;
logic                                   dat_obank_code_m3_en;
logic [6:0]                             dat_obank_code_m3_d ;
logic [6:0]                             dat_obank_code_m3_q ;

logic                                   dff_tag_ecc_mask_m3_en;
logic [WAY_N-1:0]                       dff_tag_ecc_mask_m3_d ;
logic [WAY_N-1:0]                       dff_tag_ecc_mask_m3_q ;
logic                                   dff_ebank_ecc_mask_m3_en;
logic [WAY_N-1:0]                       dff_ebank_ecc_mask_m3_d ;
logic [WAY_N-1:0]                       dff_ebank_ecc_mask_m3_q ;
logic                                   dff_obank_ecc_mask_m3_en;
logic [WAY_N-1:0]                       dff_obank_ecc_mask_m3_d ;
logic [WAY_N-1:0]                       dff_obank_ecc_mask_m3_q ;

logic                                   dff_ld0_vld_m3_set;
logic                                   dff_ld0_vld_m3_clr;
logic                                   dff_ld0_vld_m3_en ;
logic                                   dff_ld0_vld_m3_d  ;
logic                                   dff_ld0_vld_m3_q  ;
logic                                   dff_ld1_vld_m3_set;
logic                                   dff_ld1_vld_m3_clr;
logic                                   dff_ld1_vld_m3_en ;
logic                                   dff_ld1_vld_m3_d  ;
logic                                   dff_ld1_vld_m3_q  ;
logic                                   dff_ld1_samecl_m3_en ;
logic                                   dff_ld1_samecl_m3_d  ;
logic                                   dff_ld1_samecl_m3_q  ;
logic                                   dff_ld0_id_m3_en;
logic [ID_EXT_W-1:0]                    dff_ld0_id_m3_d ;
logic [ID_EXT_W-1:0]                    dff_ld0_id_m3_q ;
logic                                   dff_ld0_in_obank_en;
logic                                   dff_ld0_in_obank_d ;
logic                                   dff_ld0_in_obank_q ;
logic                                   dff_ld1_in_obank_en;
logic                                   dff_ld1_in_obank_d ;
logic                                   dff_ld1_in_obank_q ;
logic                                   dff_ld_src_m3_en   ;
logic                                   dff_ld_src_m3_d    ;
logic                                   dff_ld_src_m3_q    ;
logic                                   ebank_need_ecc_m2  ;
logic                                   obank_need_ecc_m2  ;

logic [MB_ENT_N-1:0][WAY_N  -1:0]       dff_mb_ecccode_en;
logic [MB_ENT_N-1:0][WAY_N*2*7-1:0]     dff_mb_ecccode_d ;
logic [MB_ENT_N-1:0][WAY_N*2*7-1:0]     dff_mb_ecccode_q ;
logic [WAY_N*2*7-1:0]                   lfl_code;
logic [WAY_N*2*7-1:0]                   eb_w_mux_code;
logic [WAY_N*2*7-1:0]                   evc_line_raw_code;
logic [MBUS_WORD_N*7-1:0]               bmu_dcu_r_code   ;
logic [WAY_N*2*7-1:0]                   refill_line_code ;

logic [MBUS_WORD_N-1:0][6:0]            eb_code;
logic [MBUS_WORD_N-1:0][38:0]           eb_data_predec;
logic [MBUS_WORD_N-1:0][38:0]           eb_data_postdec;
logic [MBUS_WORD_N-1:0]                 eb_data_err;
logic [MBUS_WORD_N-1:0]                 eb_data_sec;
logic [MBUS_WORD_N-1:0]                 eb_data_ded;

logic                                   ld0_raserr_m3;
logic                                   ld0_replay_m3;
logic                                   ld1_raserr_m3;
logic                                   ld1_replay_m3;

logic                                   ecc_inject_tag;
logic                                   ecc_inject_dat;

// ECC injection

assign ecc_inject_tag = csr_inject_en & (csr_inject_dest == 4'b0010);
assign ecc_inject_dat = csr_inject_en & (csr_inject_dest == 4'b0011);
 
for (genvar i=0; i<WAY_N; i++) begin : g_ecc_inject
//tag_ram inject（20:0）
  assign ram_tag_rdata[i][TAG_FULL_W-1:0] = (ecc_inject_tag & csr_inject_bank[i]) // ECC错误注入使能信号 && 选择要注入错误的特定bank
                                          ? (dcu_ram_tag_rdata[i][TAG_FULL_W-1:0] ^ csr_inject_bitmapl[TAG_FULL_W-1:0]) //通过异或操作在原始数据中注入错误
                                          : dcu_ram_tag_rdata[i][TAG_FULL_W-1:0]; //当不进行错误注入时，直接返回原始数据
//tag_ram inject（28:21）
  assign ram_tag_rdata[i][TAG_RAM_D_W-1:TAG_FULL_W] = dcu_ram_tag_rdata[i][TAG_RAM_D_W-1:TAG_FULL_W]; //ECC校验位保持不变，不进行错误注入                

  assign ram_data0_rdata[i][31:0] = (ecc_inject_dat & csr_inject_bank[i]) //data0 ECC错误注入使能
                                  ? (dcu_ram_data0_rdata[i][31:0] ^ csr_inject_bitmapl[31:0])  //data0 ECC错误注入
                                  : dcu_ram_data0_rdata[i][31:0]; //当不进行错误注入时，直接返回原始数据
//data_ram inject
  assign ram_data0_rdata[i][DAT_RAM_D_W-1:32] = dcu_ram_data0_rdata[i][DAT_RAM_D_W-1:32];  //ECC校验位保持不变，不进行错误注入                
 
  assign ram_data1_rdata[i][31:0] = (ecc_inject_dat & csr_inject_bank[i])//data1 ECC错误注入使能
                                  ? (dcu_ram_data1_rdata[i][31:0] ^ csr_inject_bitmaph[31:0])//data0 ECC错误注入
                                  : dcu_ram_data1_rdata[i][31:0];//当不进行错误注入时，直接返回原始数据

  assign ram_data1_rdata[i][DAT_RAM_D_W-1:32] = dcu_ram_data1_rdata[i][DAT_RAM_D_W-1:32];//ECC校验位保持不变，不进行错误注入

  end // g_ecc_inject

  for (genvar i=0; i<WAY_N; i++) begin : g_ram_tag_dec

  // Padding tag to 32-bit to do ecc_dec
  assign ram_tag_rdata_pad_m2[i] = { ram_tag_rdata[i][TAG_ECC_BASE_BIT+:7], //The stored ECC check code， 21bit ~ 27bit
                                     {32-TAG_FULL_W{1'b0}},                 //All fill bits are 0， 28bit ~ 31bit
                                     ram_tag_rdata[i][TAG_FULL_W-1:0] };    //include valid、dirty和tag， 0bit ~ 20bit

  //tag_ram解码
  wing_cbb_ecc_dec32 u_tag_ecc_dec32 (
    .data_i         ( ram_tag_rdata_pad_m2[i] ),
    .data_o         ( ram_tag_post_dec_m2 [i] ),
    .syndrome_o     ( ),
    .error_detected ( ram_tag_err_m2[i] ),
    .single_error_o ( ram_tag_sec_m2[i] ),
    .double_error_o ( ram_tag_ded_m2[i] )
  );

  assign dff_ram_tag_post_dec_m3_d[i] = {ram_tag_post_dec_m2[i][32+:7],
                                         ram_tag_post_dec_m2[i][TAG_FULL_W-1:0]};

end // g_hitgen

assign ram_tag_ecc_sec_m2 = ram_tag_sec_m2 & dff_tag_ram_read_m2_q;  // 当tag ECC SEC请求时，使用tag ECC SEC请求器选择的bank
assign ram_tag_ecc_ded_m2 = ram_tag_ded_m2 & dff_tag_ram_read_m2_q;  // 当tag ECC SEC请求时，使用tag ECC SEC请求器选择的bank

assign tag_ecc_bang_m2 = |(ram_tag_err_m2 & dff_tag_ram_read_m2_q) & csr_ecc_en;  // 标签ECC错误M2(标签RAM错误且正在读取且ECC使能)
assign tag_ecc_sec_m2  = |ram_tag_ecc_sec_m2 & csr_ecc_en;  // 标签ECC单比特错误M2(标签RAM单比特错误且ECC使能)
assign tag_ecc_ded_m2  = |ram_tag_ecc_ded_m2 & csr_ecc_en;  // 标签ECC双比特错误M2(标签RAM双比特错误且ECC使能)

assign tag_ecc_sec_m3 = |dff_ram_tag_sec_m3_q;  // 标签ECC单比特错误M3(标签RAM单比特错误M3寄存器)
assign tag_ecc_ded_m3 = |dff_ram_tag_ded_m3_q;  // 标签ECC双比特错误M3(标签RAM双比特错误M3寄存器)

assign dff_ram_tag_sec_m3_en = tag_ecc_sec_m2 | tag_ecc_sec_m3;  // 标签RAM单比特错误M3使能(M2或M3阶段有单比特错误)
assign dff_ram_tag_sec_m3_d  = tag_ecc_sec_m2 ? ram_tag_ecc_sec_m2 : {WAY_N{1'b0}};  // 标签RAM单比特错误M3数据输入(如果有错误使用纠正数据，否则清零)

`WDFFER(dff_ram_tag_sec_m3_q, dff_ram_tag_sec_m3_d, dff_ram_tag_sec_m3_en, clk, rst_n)  // 标签RAM单比特错误M3寄存器

assign dff_ram_tag_ded_m3_en = tag_ecc_ded_m2 | tag_ecc_ded_m3;  // 标签RAM双比特错误M3使能(M2或M3阶段有双比特错误)
assign dff_ram_tag_ded_m3_d  = tag_ecc_ded_m2 ? ram_tag_ecc_ded_m2 : {WAY_N{1'b0}};  // 标签RAM双比特错误M3数据输入(如果有错误使用纠正数据，否则清零)

`WDFFER(dff_ram_tag_ded_m3_q, dff_ram_tag_ded_m3_d, dff_ram_tag_ded_m3_en, clk, rst_n)  // 标签RAM双比特错误M3寄存器
 
assign dff_ram_tag_post_dec_m3_en = tag_ecc_bang_m2;  // 标签RAM后解码M3使能(标签ECC错误M2)
// dff_ram_tag_post_dec_m3_d在上面的for循环中定义。

`WDFFER(dff_ram_tag_post_dec_m3_q, dff_ram_tag_post_dec_m3_d, dff_ram_tag_post_dec_m3_en, clk, rst_n)  // 标签RAM后解码M3寄存器

assign dff_tag_setidx_m3_en = tag_ecc_bang_m2;  // 标签集合索引M3使能(标签ECC错误M2)
assign dff_tag_setidx_m3_d  = dff_addr_m2_q[5+:SETIDX_W];  // 标签集合索引M3数据输入(M2地址的集合索引部分)

`WDFFER(dff_tag_setidx_m3_q, dff_tag_setidx_m3_d, dff_tag_setidx_m3_en, clk, rst_n)  // 标签集合索引M3寄存器


assign dff_ebank_touched_m2_set = |(dcu_ram_data0_cs & ~dcu_ram_data0_wr);  // 偶数bank被访问M2设置(数据0片选且非写操作)
assign dff_ebank_touched_m2_clr = dff_ebank_touched_m2_q;  // 偶数bank被访问M2清除
assign dff_ebank_touched_m2_en  = dff_ebank_touched_m2_set | dff_ebank_touched_m2_clr;  // 偶数bank被访问M2使能
assign dff_ebank_touched_m2_d   = dff_ebank_touched_m2_set;  // 偶数bank被访问M2数据输入

assign dff_obank_touched_m2_set = |(dcu_ram_data1_cs & ~dcu_ram_data1_wr);  // 奇数bank被访问M2设置(数据1片选且非写操作)
assign dff_obank_touched_m2_clr = dff_obank_touched_m2_q;  // 奇数bank被访问M2清除
assign dff_obank_touched_m2_en  = dff_obank_touched_m2_set | dff_obank_touched_m2_clr;  // 奇数bank被访问M2使能
assign dff_obank_touched_m2_d   = dff_obank_touched_m2_set;  // 奇数bank被访问M2数据输入

`WDFFER(dff_ebank_touched_m2_q, dff_ebank_touched_m2_d, dff_ebank_touched_m2_en, clk, rst_n)  // 偶数bank被访问M2寄存器
`WDFFER(dff_obank_touched_m2_q, dff_obank_touched_m2_d, dff_obank_touched_m2_en, clk, rst_n)  // 奇数bank被访问M2寄存器

// 'data_ebank_pick_nohg'包括UTAG和STD情况，
// 检查已注册的'dff_ebank_touched_m2_q'就足够了。
// 但是，加载数据需要检查命中。只有命中的加载数据才会在M3阶段解码ECC。

// 对于加载0情况，不涉及加载取消。
// 即使加载请求在M2阶段被取消，M3仍会纠正ECC SEC。

assign ebank_need_ecc_m2 = dff_ebank_touched_m2_q &  // 偶数bank在M2阶段被访问的标志
                           ( data_ebank_pick_nohg |  // 表示LD1和STD读对应读取的偶数bank在M2阶段被访问，所以需要进行ECC SEC请求
                             dff_dat_type_m2_q[DAT_TYPE_LD] & ld0_tag_is_hit & ~tag_ecc_bang_m2 &  // 当前是加载操作且偶数bank命中且不是tag ECC SEC请求
                             (~dff_addr_m2_q[2] | dff_samecl_m2_q & ~dff_ld1_addr_m2_q[2]) );     // 加载0在偶数bank上或偶数bank和加载1地址在同一个缓存行中且加载1在偶数bank上

assign obank_need_ecc_m2 = dff_obank_touched_m2_q &  // 奇数bank在M2阶段被访问的标志
                           ( data_obank_pick_nohg |  // 表示LD1和STD读对应读取的奇数bank在M2阶段被访问，所以需要进行ECC SEC请求
                             dff_dat_type_m2_q[DAT_TYPE_LD] & ld0_tag_is_hit & ~tag_ecc_bang_m2 &  // 当前是加载操作且奇数bank命中且不是tag ECC SEC请求
                             (dff_addr_m2_q[2] | dff_samecl_m2_q & dff_ld1_addr_m2_q[2]) );     // 加载0在奇数bank上或奇数bank和加载1地址在同一个缓存行中且加载1在奇数bank上

assign dff_std_addr_m2_en = sbu_dcu_data_req_vld_m1 & dcu_sbu_data_req_rdy_m1;  // STD地址M2使能
assign dff_std_addr_m2_d  = sbu_dcu_data_req_addr_m1;                             // STD地址M2数据输入

`WDFFER(dff_std_addr_m2_q, dff_std_addr_m2_d, dff_std_addr_m2_en, clk, rst_n)  // STD地址M2寄存器

// STC和加载0地址注册在dff_addr_m2_q中，
// 加载1地址注册在dff_ld1_addr_m2_q中。
assign ebank_addr_m2 = dff_dat_type_m2_q[DAT_TYPE_STD]   ? dff_std_addr_m2_q :  // 当STD读时，使用STD的读地址纠错
                       (~dff_addr_m2_q[2] & no_ncdvc_m2) ? dff_addr_m2_q     :  // 当加载0或STD读在偶数bank上时，使用加载0的地址纠错
                                                           dff_ld1_addr_m2_q ;  // 使用加载1的地址纠错

assign obank_addr_m2 = dff_dat_type_m2_q[DAT_TYPE_STD]  ? dff_std_addr_m2_q :  // 当STD读时，使用STD的读地址纠错
                       (dff_addr_m2_q[2] & no_ncdvc_m2) ? dff_addr_m2_q     :  // 当加载0或STD读在奇数bank上时，使用加载0的地址纠错
                                                          dff_ld1_addr_m2_q ;  // 使用加载1的地址纠错

// 偶数bank和奇数bank地址/bank_pair应该分别注册，
// 因为加载[0]主标签和加载[1] utag访问不同的缓存行集合。

assign dff_ebank_addr_m3_en = ebank_need_ecc_m2;  // 偶数bank地址M3使能
assign dff_ebank_addr_m3_d  = ebank_addr_m2;       // 偶数bank地址M3数据输入
 
assign dff_obank_addr_m3_en = obank_need_ecc_m2;  // 奇数bank地址M3使能
assign dff_obank_addr_m3_d  = obank_addr_m2;       // 奇数bank地址M3数据输入

`WDFFER(dff_ebank_addr_m3_q, dff_ebank_addr_m3_d, dff_ebank_addr_m3_en, clk, rst_n)  // 偶数bank地址M3寄存器
`WDFFER(dff_obank_addr_m3_q, dff_obank_addr_m3_d, dff_obank_addr_m3_en, clk, rst_n)  // 奇数bank地址M3寄存器
 
assign dat_ebank_pair_m3_en = ebank_need_ecc_m2;  // 数据偶数bank配对M3使能
assign dat_ebank_pair_m3_d  = dat_ebank_sel;       // 数据偶数bank配对M3数据输入

assign dat_obank_pair_m3_en = obank_need_ecc_m2;  // 数据奇数bank配对M3使能
assign dat_obank_pair_m3_d  = dat_obank_sel;       // 数据奇数bank配对M3数据输入

`WDFFER(dat_ebank_pair_m3_q, dat_ebank_pair_m3_d, dat_ebank_pair_m3_en, clk, rst_n)  // 数据偶数bank配对M3寄存器
`WDFFER(dat_obank_pair_m3_q, dat_obank_pair_m3_d, dat_obank_pair_m3_en, clk, rst_n)  // 数据奇数bank配对M3寄存器

// 边界情况：如果M2读bank和M1行填充/STD写bank在同一周期是相同的集合和相同的路，
// M3 ECC纠正在下一周期将覆盖上一周期的M1写数据。
// 因此，检查这种情况以取消M3 ECC纠正自写。
// 但RAS报告仍在工作。


assign dff_tag_ecc_mask_m3_en = tag_ecc_bang_m2;  //当tag出现错误时，mask的信号进行打拍
assign dff_tag_ecc_mask_m3_d  = {WAY_N{pab_tag_setidx == dff_addr_m2_q[5+:SETIDX_W]}} & //检查PAB（pre-arbiter）的tag set索引是否与M2阶段的地址set索引匹配
                                ( lfl_tag_bank_cs        & {WAY_N{lfl_wr_rdy}} //检测是否有linefill操作正在写入tag RAM
                                | sbu_dcu_data_req_wayoh & {WAY_N{dcu_sbu_data_req_rdy_m1 & pab_tag_req[PAB_TAG_STD]}} ); //检测是否有STD操作正在写入tag RAM

`WDFFER(dff_tag_ecc_mask_m3_q, dff_tag_ecc_mask_m3_d, dff_tag_ecc_mask_m3_en, clk, rst_n) // 哪些ecc需要mask的信号进行打拍

assign dff_ebank_ecc_mask_m3_en = ebank_need_ecc_m2; // 偶数bank在发生ecc error时需要ecc mask的信号进行打拍
assign dff_ebank_ecc_mask_m3_d  = {WAY_N{pab_dat_setidx == ebank_addr_m2[5 +: SETIDX_W]}} & //检查PAB数据set索引是否与M2阶段偶数bank地址的set索引匹配
                                  { (pab_data_bank67_offs == ebank_addr_m2[4:3]), //检查PAB数据bank67是否与M2阶段偶数bank地址的bank匹配
                                    (pab_data_bank45_offs == ebank_addr_m2[4:3]), //检查PAB数据bank45是否与M2阶段偶数bank地址的bank匹配
                                    (pab_data_bank23_offs == ebank_addr_m2[4:3]), //检查PAB数据bank23是否与M2阶段偶数bank地址的bank匹配
                                    (pab_data_bank01_offs == ebank_addr_m2[4:3]) } & //检查PAB数据bank01是否与M2阶段偶数bank地址的bank匹配
                                  ( lfl_dat_bank_cs       & {WAY_N{lfl_wr_rdy}} //检测是否有linefill操作正在写入数据RAM，lfl写与ecc写在同一周期
                                  | sbu_data_req_ebank_cs & {WAY_N{dcu_sbu_data_req_rdy_m1 & sbu_dcu_data_req_type_m1}} ); //检测是否有SBU操作正在写入数据RAM，lfl写与ecc写在同一周期

`WDFFER(dff_ebank_ecc_mask_m3_q, dff_ebank_ecc_mask_m3_d, dff_ebank_ecc_mask_m3_en, clk, rst_n)

assign dff_obank_ecc_mask_m3_en = obank_need_ecc_m2; // 奇数bank在发生ecc error时需要ecc mask的信号进行打拍
assign dff_obank_ecc_mask_m3_d  = {WAY_N{pab_dat_setidx == obank_addr_m2[5 +: SETIDX_W]}} &  // 奇数bank ECC掩码数据输入(检查PAB数据set索引是否与M2阶段奇数bank地址的set索引匹配)
                                  { (pab_data_bank67_offs == obank_addr_m2[4:3]),  // 检查PAB数据bank67是否与M2阶段奇数bank地址的bank匹配
                                    (pab_data_bank45_offs == obank_addr_m2[4:3]),  // 检查PAB数据bank45是否与M2阶段奇数bank地址的bank匹配
                                    (pab_data_bank23_offs == obank_addr_m2[4:3]),  // 检查PAB数据bank23是否与M2阶段奇数bank地址的bank匹配
                                    (pab_data_bank01_offs == obank_addr_m2[4:3]) } &  // 检查PAB数据bank01是否与M2阶段奇数bank地址的bank匹配
                                  ( lfl_dat_bank_cs       & {WAY_N{lfl_wr_rdy}}  // 检测是否有linefill操作正在写入数据RAM，lfl写与ecc写在同一周期
                                  | sbu_data_req_obank_cs & {WAY_N{dcu_sbu_data_req_rdy_m1 & sbu_dcu_data_req_type_m1}} );  // 检测是否有SBU操作正在写入数据RAM，SBU写与ecc写在同一周期

`WDFFER(dff_obank_ecc_mask_m3_q, dff_obank_ecc_mask_m3_d, dff_obank_ecc_mask_m3_en, clk, rst_n)  // 奇数bank ECC掩码寄存器

// 接口的流水线信息M3

assign dff_ld0_vld_m3_set = ld0_real_vld_m2 & (tag_ecc_bang_m2 | ld0_hit_m2);  // 加载0有效M3设置(真实有效且标签ECC错误或命中)
assign dff_ld0_vld_m3_clr = dff_ld0_vld_m3_q;  // 加载0有效M3清除
assign dff_ld0_vld_m3_en  = dff_ld0_vld_m3_set | dff_ld0_vld_m3_clr;  // 加载0有效M3使能
assign dff_ld0_vld_m3_d   = dff_ld0_vld_m3_set ;  // 加载0有效M3数据输入
 
`WDFFER(dff_ld0_vld_m3_q, dff_ld0_vld_m3_d, dff_ld0_vld_m3_en, clk, rst_n)  // 加载0有效M3寄存器

assign dff_ld1_vld_m3_set = ld1_real_vld_m2 & (dff_samecl_m2_q & tag_ecc_bang_m2 |  // 加载1有效M3设置(真实有效且相同缓存行且标签ECC错误或命中)
                                               ld1_hit_m2);
assign dff_ld1_vld_m3_clr = dff_ld1_vld_m3_q;  // 加载1有效M3清除
assign dff_ld1_vld_m3_en  = dff_ld1_vld_m3_set | dff_ld1_vld_m3_clr;  // 加载1有效M3使能
assign dff_ld1_vld_m3_d   = dff_ld1_vld_m3_set ;  // 加载1有效M3数据输入

`WDFFER(dff_ld1_vld_m3_q, dff_ld1_vld_m3_d, dff_ld1_vld_m3_en, clk, rst_n)  // 加载1有效M3寄存器
 
assign dff_ld1_samecl_m3_en = dff_ld1_vld_m3_set;  // 加载1相同缓存行M3使能
assign dff_ld1_samecl_m3_d  = dff_samecl_m2_q;     // 加载1相同缓存行M3数据输入

`WDFFER(dff_ld1_samecl_m3_q, dff_ld1_samecl_m3_d, dff_ld1_samecl_m3_en, clk, rst_n)  // 加载1相同缓存行M3寄存器
 
assign dff_ld0_id_m3_en = dff_dat_type_m2_q[DAT_TYPE_LD];  // 加载0 ID M3使能
assign dff_ld0_id_m3_d  = dff_tag_id_m2_q;                 // 加载0 ID M3数据输入
 
`WDFFER(dff_ld0_id_m3_q, dff_ld0_id_m3_d, dff_ld0_id_m3_en, clk, rst_n)  // 加载0 ID M3寄存器
 
assign dff_ld0_in_obank_en = dff_dat_type_m2_q[DAT_TYPE_LD];  // 加载0在奇数bank M3使能
assign dff_ld0_in_obank_d  = dff_addr_m2_q[2];                 // 加载0在奇数bank M3数据输入

`WDFFER(dff_ld0_in_obank_q, dff_ld0_in_obank_d, dff_ld0_in_obank_en, clk, rst_n)  // 加载0在奇数bank M3寄存器
 
assign dff_ld1_in_obank_en = dff_dat_type_m2_q[DAT_TYPE_LD];  // 加载1在奇数bank M3使能
assign dff_ld1_in_obank_d  = dff_ld1_addr_m2_q[2];            // 加载1在奇数bank M3数据输入

`WDFFER(dff_ld1_in_obank_q, dff_ld1_in_obank_d, dff_ld1_in_obank_en, clk, rst_n)  // 加载1在奇数bank M3寄存器
 
// 0: 来自VPU的加载请求
// 1: 来自LSU的加载请求
assign dff_ld_src_m3_en = dff_dat_type_m2_q[DAT_TYPE_LD];  // 加载源M3使能
assign dff_ld_src_m3_d  = ld_src_m2;                        // 加载源M3数据输入

`WDFFER(dff_ld_src_m3_q, dff_ld_src_m3_d, dff_ld_src_m3_en, clk, rst_n)  // 加载源M3寄存器
 
 
if (DATA_ECC_MODE == 0) begin : g_data_ecc_m2  // 数据ECC模式0：在M2阶段进行数据bank ECC解码

  //**********************************
  // 数据bank ECC在M2阶段解码
  //**********************************

  logic [DAT_RAM_D_W-1:0]                 ebank_dec_datao_m2;  // 偶数bank解码数据输出M2
  logic                                   ebank_dec_sec_m2  ;  // 偶数bank解码单比特错误M2
  logic                                   ebank_dec_ded_m2  ;  // 偶数bank解码双比特错误M2
  logic [DAT_RAM_D_W-1:0]                 obank_dec_datao_m2;  // 奇数bank解码数据输出M2
  logic                                   obank_dec_sec_m2  ;  // 奇数bank解码单比特错误M2
  logic                                   obank_dec_ded_m2  ;  // 奇数bank解码双比特错误M2
  logic                                   dff_ebank_sec_m3_set;  // 偶数bank单比特错误M3设置
  logic                                   dff_ebank_sec_m3_clr;  // 偶数bank单比特错误M3清除
  logic                                   dff_ebank_sec_m3_en ;  // 偶数bank单比特错误M3使能
  logic                                   dff_ebank_sec_m3_d  ;  // 偶数bank单比特错误M3数据输入
  logic                                   dff_ebank_sec_m3_q  ;  // 偶数bank单比特错误M3寄存器
  logic                                   dff_ebank_ded_m3_set;  // 偶数bank双比特错误M3设置
  logic                                   dff_ebank_ded_m3_clr;  // 偶数bank双比特错误M3清除
  logic                                   dff_ebank_ded_m3_en ;  // 偶数bank双比特错误M3使能
  logic                                   dff_ebank_ded_m3_d  ;  // 偶数bank双比特错误M3数据输入
  logic                                   dff_ebank_ded_m3_q  ;  // 偶数bank双比特错误M3寄存器
  logic                                   dff_obank_sec_m3_set;  // 奇数bank单比特错误M3设置
  logic                                   dff_obank_sec_m3_clr;  // 奇数bank单比特错误M3清除
  logic                                   dff_obank_sec_m3_en ;  // 奇数bank单比特错误M3使能
  logic                                   dff_obank_sec_m3_d  ;  // 奇数bank单比特错误M3数据输入
  logic                                   dff_obank_sec_m3_q  ;  // 奇数bank单比特错误M3寄存器
  logic                                   dff_obank_ded_m3_set;  // 奇数bank双比特错误M3设置
  logic                                   dff_obank_ded_m3_clr;  // 奇数bank双比特错误M3清除
  logic                                   dff_obank_ded_m3_en ;  // 奇数bank双比特错误M3使能
  logic                                   dff_obank_ded_m3_d  ;  // 奇数bank双比特错误M3数据输入
  logic                                   dff_obank_ded_m3_q  ;  // 奇数bank双比特错误M3寄存器

  wing_cbb_ecc_dec32 u_ebank_ecc_dec32 (  // 偶数bank ECC解码器32位
    .data_i         ( full_data_ebank_m2 ),  // 输入数据(完整偶数bank数据M2)
    .data_o         ( ebank_dec_datao_m2 ),  // 输出数据(偶数bank解码数据输出M2)
    .syndrome_o     (                    ),  // 症状输出(未使用)
    .error_detected (                    ),  // 错误检测(未使用)
    .single_error_o ( ebank_dec_sec_m2   ),  // 单比特错误输出(偶数bank解码单比特错误M2)
    .double_error_o ( ebank_dec_ded_m2   )   // 双比特错误输出(偶数bank解码双比特错误M2)
  );
 
  wing_cbb_ecc_dec32 u_obank_ecc_dec32 (  // 奇数bank ECC解码器32位
    .data_i         ( full_data_obank_m2 ),  // 输入数据(完整奇数bank数据M2)
    .data_o         ( obank_dec_datao_m2 ),  // 输出数据(奇数bank解码数据输出M2)
    .syndrome_o     (                    ),  // 症状输出(未使用)
    .error_detected (                    ),  // 错误检测(未使用)
    .single_error_o ( obank_dec_sec_m2   ),  // 单比特错误输出(奇数bank解码单比特错误M2)
    .double_error_o ( obank_dec_ded_m2   )   // 双比特错误输出(奇数bank解码双比特错误M2)
  );

  assign dff_ebank_sec_m3_set = ebank_need_ecc_m2 & ebank_dec_sec_m2 & csr_ecc_en;  // 偶数bank单比特错误M3设置(需要ECC且解码单比特错误且ECC使能)
  assign dff_ebank_sec_m3_clr = dff_ebank_sec_m3_q;  // 偶数bank单比特错误M3清除
  assign dff_ebank_sec_m3_en  = dff_ebank_sec_m3_set | dff_ebank_sec_m3_clr;  // 偶数bank单比特错误M3使能
  assign dff_ebank_sec_m3_d   = dff_ebank_sec_m3_set;  // 偶数bank单比特错误M3数据输入
 
  `WDFFER(dff_ebank_sec_m3_q, dff_ebank_sec_m3_d, dff_ebank_sec_m3_en, clk, rst_n)  // 偶数bank单比特错误M3寄存器

  assign dff_ebank_ded_m3_set = ebank_need_ecc_m2 & ebank_dec_ded_m2 & csr_ecc_en;  // 偶数bank双比特错误M3设置(需要ECC且解码双比特错误且ECC使能)
  assign dff_ebank_ded_m3_clr = dff_ebank_ded_m3_q;  // 偶数bank双比特错误M3清除
  assign dff_ebank_ded_m3_en  = dff_ebank_ded_m3_set | dff_ebank_ded_m3_clr;  // 偶数bank双比特错误M3使能
  assign dff_ebank_ded_m3_d   = dff_ebank_ded_m3_set;  // 偶数bank双比特错误M3数据输入
 
  `WDFFER(dff_ebank_ded_m3_q, dff_ebank_ded_m3_d, dff_ebank_ded_m3_en, clk, rst_n)  // 偶数bank双比特错误M3寄存器

  assign dff_obank_sec_m3_set = obank_need_ecc_m2 & obank_dec_sec_m2 & csr_ecc_en;  // 奇数bank单比特错误M3设置(需要ECC且解码单比特错误且ECC使能)
  assign dff_obank_sec_m3_clr = dff_obank_sec_m3_q;  // 奇数bank单比特错误M3清除
  assign dff_obank_sec_m3_en  = dff_obank_sec_m3_set | dff_obank_sec_m3_clr;  // 奇数bank单比特错误M3使能
  assign dff_obank_sec_m3_d   = dff_obank_sec_m3_set;  // 奇数bank单比特错误M3数据输入
 
  `WDFFER(dff_obank_sec_m3_q, dff_obank_sec_m3_d, dff_obank_sec_m3_en, clk, rst_n)  // 奇数bank单比特错误M3寄存器

  assign dff_obank_ded_m3_set = obank_need_ecc_m2 & obank_dec_ded_m2 & csr_ecc_en;  // 奇数bank双比特错误M3设置(需要ECC且解码双比特错误且ECC使能)
  assign dff_obank_ded_m3_clr = dff_obank_ded_m3_q;  // 奇数bank双比特错误M3清除
  assign dff_obank_ded_m3_en  = dff_obank_ded_m3_set | dff_obank_ded_m3_clr;  // 奇数bank双比特错误M3使能
  assign dff_obank_ded_m3_d   = dff_obank_ded_m3_set;  // 奇数bank双比特错误M3数据输入
 
  `WDFFER(dff_obank_ded_m3_q, dff_obank_ded_m3_d, dff_obank_ded_m3_en, clk, rst_n)  // 奇数bank双比特错误M3寄存器

  assign dat_ebank_code_m3_en = ebank_need_ecc_m2;  // 数据偶数bank纠错码M3使能
  assign dat_ebank_code_m3_d  = ebank_dec_datao_m2[DAT_RAM_D_W-1 : 32];  // 数据偶数bank纠错码M3数据输入(偶数bank解码数据输出的纠错码部分)

  assign dat_obank_code_m3_en = obank_need_ecc_m2;  // 数据奇数bank纠错码M3使能
  assign dat_obank_code_m3_d  = obank_dec_datao_m2[DAT_RAM_D_W-1 : 32];  // 数据奇数bank纠错码M3数据输入(奇数bank解码数据输出的纠错码部分)

  `WDFFER(dat_ebank_code_m3_q, dat_ebank_code_m3_d, dat_ebank_code_m3_en, clk, rst_n)  // 数据偶数bank纠错码M3寄存器
  `WDFFER(dat_obank_code_m3_q, dat_obank_code_m3_d, dat_obank_code_m3_en, clk, rst_n)  // 数据奇数bank纠错码M3寄存器

  assign ebank_data_m3 = {dat_ebank_code_m3_q, dff_ebank_data_m3_q};  // 偶数bank数据M3(纠错码和数据拼接)
  assign obank_data_m3 = {dat_obank_code_m3_q, dff_obank_data_m3_q};  // 奇数bank数据M3(纠错码和数据拼接)

  assign ebank_ecc_sec_m3 = dff_ebank_sec_m3_q;  // 偶数bank ECC单比特错误M3
  assign obank_ecc_sec_m3 = dff_obank_sec_m3_q;  // 奇数bank ECC单比特错误M3
 
  assign ebank_ecc_ded_m3 = dff_ebank_ded_m3_q;  // 偶数bank ECC双比特错误M3
  assign obank_ecc_ded_m3 = dff_obank_ded_m3_q;  // 奇数bank ECC双比特错误M3

  assign dff_ebank_data_m3_d = ebank_dec_datao_m2[31:0];  // 偶数bank数据M3数据输入(偶数bank解码数据输出的数据部分)
  assign dff_obank_data_m3_d = obank_dec_datao_m2[31:0];  // 奇数bank数据M3数据输入(奇数bank解码数据输出的数据部分)


end else if (DATA_ECC_MODE == 1) begin : g_data_ecc_m3

  //**********************************
  // data bank ECC decode at M3 stage
  //**********************************
  // 数据ECC模式1：在M3阶段进行数据bank ECC解码
  //**********************************

  logic                                   dff_ebank_need_ecc_m3_set;  // 偶数bank需要ECC M3设置
  logic                                   dff_ebank_need_ecc_m3_clr;  // 偶数bank需要ECC M3清除
  logic                                   dff_ebank_need_ecc_m3_en ;  // 偶数bank需要ECC M3使能
  logic                                   dff_ebank_need_ecc_m3_d  ;  // 偶数bank需要ECC M3数据输入
  logic                                   dff_ebank_need_ecc_m3_q  ;  // 偶数bank需要ECC M3寄存器
  logic                                   dff_obank_need_ecc_m3_set;  // 奇数bank需要ECC M3设置
  logic                                   dff_obank_need_ecc_m3_clr;  // 奇数bank需要ECC M3清除
  logic                                   dff_obank_need_ecc_m3_en ;  // 奇数bank需要ECC M3使能
  logic                                   dff_obank_need_ecc_m3_d  ;  // 奇数bank需要ECC M3数据输入
  logic                                   dff_obank_need_ecc_m3_q  ;  // 奇数bank需要ECC M3寄存器
  logic [DAT_RAM_D_W-1:0]                 ebank_dec_datai_m3;  // 偶数bank解码数据输入M3
  logic [DAT_RAM_D_W-1:0]                 ebank_dec_datao_m3;  // 偶数bank解码数据输出M3
  logic                                   ebank_dec_sec_m3;  // 偶数bank解码单比特错误M3
  logic                                   ebank_dec_ded_m3;  // 偶数bank解码双比特错误M3
  logic [DAT_RAM_D_W-1:0]                 obank_dec_datai_m3;  // 奇数bank解码数据输入M3
  logic [DAT_RAM_D_W-1:0]                 obank_dec_datao_m3;  // 奇数bank解码数据输出M3
  logic                                   obank_dec_sec_m3;  // 奇数bank解码单比特错误M3
  logic                                   obank_dec_ded_m3;  // 奇数bank解码双比特错误M3
 
  assign dff_ebank_need_ecc_m3_set = ebank_need_ecc_m2; //ebank的ecc的set信号
  assign dff_ebank_need_ecc_m3_clr = dff_ebank_need_ecc_m3_q; //ebank的ecc的使能的清零信息
  assign dff_ebank_need_ecc_m3_en  = dff_ebank_need_ecc_m3_set | dff_ebank_need_ecc_m3_clr; //ebank的ecc的使能信号
  assign dff_ebank_need_ecc_m3_d   = dff_ebank_need_ecc_m3_set ; //ebank的ecc的使能信号
 
  assign dff_obank_need_ecc_m3_set = obank_need_ecc_m2; //obank的ecc的set信号
  assign dff_obank_need_ecc_m3_clr = dff_obank_need_ecc_m3_q;  //obank的ecc的使能的清零信息
  assign dff_obank_need_ecc_m3_en  = dff_obank_need_ecc_m3_set | dff_obank_need_ecc_m3_clr; //obank的ecc的使能信号
  assign dff_obank_need_ecc_m3_d   = dff_obank_need_ecc_m3_set ; //obank的ecc的使能信号

  `WDFFER(dff_ebank_need_ecc_m3_q, dff_ebank_need_ecc_m3_d, dff_ebank_need_ecc_m3_en, clk, rst_n) //将ebank的ecc的使能信号打一拍
  `WDFFER(dff_obank_need_ecc_m3_q, dff_obank_need_ecc_m3_d, dff_obank_need_ecc_m3_en, clk, rst_n) //将obank的ecc的使能信号打一拍

  assign dat_ebank_code_m3_en = ebank_need_ecc_m2; //ebank的ecc的使能信号作为 code 打拍的使能信号
  assign dat_ebank_code_m3_d  = full_data_ebank_m2[DAT_RAM_D_W-1 : 32]; //从ebank_ram读出来的数据的code部分
 
  assign dat_obank_code_m3_en = obank_need_ecc_m2; //obank的ecc的使能信号作为 code 打拍的使能信号
  assign dat_obank_code_m3_d  = full_data_obank_m2[DAT_RAM_D_W-1 : 32]; //从obank_ram读出来的数据的code部分

  `WDFFER(dat_ebank_code_m3_q, dat_ebank_code_m3_d, dat_ebank_code_m3_en, clk, rst_n) //将ebank 编码的code打一拍
  `WDFFER(dat_obank_code_m3_q, dat_obank_code_m3_d, dat_obank_code_m3_en, clk, rst_n) //将obank 编码的code打一拍

  assign ebank_dec_datai_m3 = {dat_ebank_code_m3_q, dff_ebank_data_m3_q}; //将ebank的code和data进行组合编码
  assign obank_dec_datai_m3 = {dat_obank_code_m3_q, dff_obank_data_m3_q}; //将obank的code和data进行组合编码

//将ebank数据进行解码
  wing_cbb_ecc_dec32 u_ebank_ecc_dec32 (
    .data_i         ( ebank_dec_datai_m3 ),
    .data_o         ( ebank_dec_datao_m3 ),
    .syndrome_o     (                    ),
    .error_detected (                    ),
    .single_error_o ( ebank_dec_sec_m3   ),
    .double_error_o ( ebank_dec_ded_m3   )
  );

//将obank数据进行解码
  wing_cbb_ecc_dec32 u_obank_ecc_dec32 (
    .data_i         ( obank_dec_datai_m3 ),
    .data_o         ( obank_dec_datao_m3 ),
    .syndrome_o     (                    ),
    .error_detected (                    ),
    .single_error_o ( obank_dec_sec_m3   ),
    .double_error_o ( obank_dec_ded_m3   )
  );

  assign ebank_data_m3 = ebank_dec_datao_m3;  // 偶数bank数据M3(来自ECC解码器输出)
  assign obank_data_m3 = obank_dec_datao_m3;  // 奇数bank数据M3(来自ECC解码器输出)
 
  assign ebank_ecc_sec_m3 = dff_ebank_need_ecc_m3_q & ebank_dec_sec_m3 & csr_ecc_en;  // 偶数bank单比特ECC错误M3(需要ECC且解码器检测到SEC且ECC使能)
  assign obank_ecc_sec_m3 = dff_obank_need_ecc_m3_q & obank_dec_sec_m3 & csr_ecc_en;  // 奇数bank单比特ECC错误M3(需要ECC且解码器检测到SEC且ECC使能)

  assign ebank_ecc_ded_m3 = dff_ebank_need_ecc_m3_q & ebank_dec_ded_m3 & csr_ecc_en;  // 偶数bank双比特ECC错误M3(需要ECC且解码器检测到DED且ECC使能)
  assign obank_ecc_ded_m3 = dff_obank_need_ecc_m3_q & obank_dec_ded_m3 & csr_ecc_en;  // 奇数bank双比特ECC错误M3(需要ECC且解码器检测到DED且ECC使能)

  assign dff_ebank_data_m3_d = full_data_ebank_m2[31:0];  // 偶数bank数据寄存器输入M3(来自M2阶段完整数据)
  assign dff_obank_data_m3_d = full_data_obank_m2[31:0];  // 奇数bank数据寄存器输入M3(来自M2阶段完整数据)
 
end     // g_data_ecc_m3

// Interface

assign ld0_raserr_m3 = tag_ecc_ded_m3 |  // tag_ram加载0 RAS错误M3(标签双比特ECC错误)
                       ~dff_ld0_in_obank_q & ebank_ecc_ded_m3 |  // 或不在奇数bank且偶数bank双比特ECC错误
                        dff_ld0_in_obank_q & obank_ecc_ded_m3 ;  // 或在奇数bank且奇数bank双比特ECC错误

assign ld0_replay_m3 = tag_ecc_sec_m3 |  // tag加载0重放M3(标签单比特ECC错误)
                       ~dff_ld0_in_obank_q & ebank_ecc_sec_m3 |  // 或不在奇数bank且偶数bank单比特ECC错误
                        dff_ld0_in_obank_q & obank_ecc_sec_m3 ;  // 或在奇数bank且奇数bank单比特ECC错误

assign ld1_raserr_m3 =  dff_ld1_samecl_m3_q & tag_ecc_ded_m3   |  // 加载1 RAS错误M3(相同缓存行tag双比特ECC错误)
                       ~dff_ld1_in_obank_q  & ebank_ecc_ded_m3 |  // 或不在奇数bank且偶数bank双比特ECC错误
                        dff_ld1_in_obank_q  & obank_ecc_ded_m3 ;  // 或在奇数bank且奇数bank双比特ECC错误

assign ld1_replay_m3 =  dff_ld1_samecl_m3_q & tag_ecc_sec_m3   |  // 加载1重放M3(相同缓存行且标签单比特ECC错误)
                       ~dff_ld1_in_obank_q  & ebank_ecc_sec_m3 |  // 或不在奇数bank且偶数bank单比特ECC错误
                        dff_ld1_in_obank_q  & obank_ecc_sec_m3 ;  // 或在奇数bank且奇数bank单比特ECC错误

assign ld_vld_m3     = dff_ld0_vld_m3_q | dff_ld1_vld_m3_q;  // 加载有效M3(加载0或加载1有效)

assign dcu_lsu_ld_rsp_valid_m3 [0] = dff_ld0_vld_m3_q & dff_ld_src_m3_q;  // DCU到LSU的加载0响应有效M3
assign dcu_lsu_ld_rsp_raserr_m3[0] = ld0_raserr_m3;  // DCU到LSU的加载0响应RAS错误M3
assign dcu_lsu_ld_rsp_replay_m3[0] = ld0_replay_m3;  // DCU到LSU的加载0响应重放M3
assign dcu_lsu_ld_rsp_lsqid_m3 [0] = dff_ld0_id_m3_q[LSQID_W-1:0];  // DCU到LSU的加载0响应LSQID M3
 
assign dcu_lsu_ld_rsp_valid_m3 [1] = dff_ld1_vld_m3_q & dff_ld_src_m3_q;  // DCU到LSU的加载1响应有效M3
assign dcu_lsu_ld_rsp_raserr_m3[1] = ld1_raserr_m3;  // DCU到LSU的加载1响应RAS错误M3
assign dcu_lsu_ld_rsp_replay_m3[1] = ld1_replay_m3;  // DCU到LSU的加载1响应重放M3
assign dcu_lsu_ld_rsp_lsqid_m3 [1] = dff_dat_id_m3_q[LSQID_W-1:0];  // DCU到LSU的加载1响应LSQID M3

`ifdef CHUNJUN_SUPPORT_V_EXT  // 支持向量扩展
 
assign dcu_vpu_ld_rsp_vld_m3   [0] = dff_ld0_vld_m3_q & ~dff_ld_src_m3_q;  // DCU到VPU的加载0响应有效M3(非LSU源)
assign dcu_vpu_ld_rsp_raserr_m3[0] = ld0_raserr_m3;  // DCU到VPU的加载0响应RAS错误M3
assign dcu_vpu_ld_rsp_replay_m3[0] = ld0_replay_m3;  // DCU到VPU的加载0响应重放M3
assign dcu_vpu_ld_rsp_vlqid_m3 [0] = dff_ld0_id_m3_q[VLQID_W-1:0];  // DCU到VPU的加载0响应VLQID M3
 
assign dcu_vpu_ld_rsp_vld_m3   [1] = dff_ld1_vld_m3_q & ~dff_ld_src_m3_q;  // DCU到VPU的加载1响应有效M3(非LSU源)
assign dcu_vpu_ld_rsp_raserr_m3[1] = ld1_raserr_m3;  // DCU到VPU的加载1响应RAS错误M3
assign dcu_vpu_ld_rsp_replay_m3[1] = ld1_replay_m3;  // DCU到VPU的加载1响应重放M3
assign dcu_vpu_ld_rsp_vlqid_m3 [1] = dff_dat_id_m3_q[VLQID_W-1:0];  // DCU到VPU的加载1响应VLQID M3

`endif  // CHUNJUN_SUPPORT_V_EXT
 
 
// ECC SEC correction request to arbiter CMO entrance.

assign ecc_arb_req      = tag_ecc_sec_m3 | // tag ECC 1bit 可纠正错误SEC请求
                          ebank_ecc_sec_m3 | obank_ecc_sec_m3 ; // bank ECC 1bit 可纠正错误SEC请求

assign ecc_tag_bank_cs  = dff_ram_tag_sec_m3_q & ~dff_tag_ecc_mask_m3_q; //ECC修正掩码，用于避免ECC修正与写操作冲突
 
assign ecc_tag_setidx = dff_tag_setidx_m3_q;
assign ecc_tag_wdata  = dff_ram_tag_post_dec_m3_q;

assign ecc_ebank_cs    = {WAY_N{ebank_ecc_sec_m3}} & dat_ebank_pair_m3_q & ~dff_ebank_ecc_mask_m3_q;
assign ecc_obank_cs    = {WAY_N{obank_ecc_sec_m3}} & dat_obank_pair_m3_q & ~dff_obank_ecc_mask_m3_q;
 
assign ecc_ebank_addr  = dff_ebank_addr_m3_q;
assign ecc_obank_addr  = dff_obank_addr_m3_q;

// Tag RAM wdata ECC encoding
wing_cbb_ecc_enc32 u_ecc_enc32_tag_wdata (
  .data_i ( {{32-TAG_FULL_W{1'b0}}, tag_wdata_preecc} ),
  .enc_o  ( tag_wdata_postecc[TAG_RAM_D_W-1 -: 7]     ) //TAG_RAM_D_W = 19+9(ecc) = 28bit ， tag_wdata_postecc[27:21]
);

assign tag_wdata_postecc[TAG_FULL_W-1:0] = tag_wdata_preecc; // tag_wdata_postecc[20:0]
 
assign ebank_ecc_pair_m3 = dat_ebank_pair_m3_q;
assign obank_ecc_pair_m3 = dat_obank_pair_m3_q;

// eviction and refill ECC code mux into MB regsiter.

assign evc_line_raw_code = {ram_data1_rdata[3][38:32],   //将memory读出的rdata的code重新排列，以便与data组合
                            ram_data0_rdata[3][38:32],
                            ram_data1_rdata[2][38:32], 
                            ram_data0_rdata[2][38:32],
                            ram_data1_rdata[1][38:32], 
                            ram_data0_rdata[1][38:32],
                            ram_data1_rdata[0][38:32], 
                            ram_data0_rdata[0][38:32] };

for (genvar i=0; i<MBUS_WORD_N; i++) begin : g_rfl_eccenc

  wing_cbb_ecc_enc32 u_ecc_enc32_r_bank (   //refill时进行的编码，将64bit分成两个32bit分别进行编码
    .data_i ( bmu_dcu_r_data[i*32 +: 32] ), //编码输入的两个32bit
    .enc_o  ( bmu_dcu_r_code[i*7  +:  7] )  //编码输出的两个7bit
  );

end // g_rfl_eccenc
 
if (MBUS_W==64) begin : g_rfl_code64

  assign refill_line_code = {LINE_LEN_N{bmu_dcu_r_code}}; //使用总线返回的数据进行编码
 
end else if (MBUS_W==128) begin : g_rfl_code128

  assign refill_line_code = (rfl_victim_way[1] | rfl_victim_way[3]) //nouse
                              ? {LINE_LEN_N{bmu_dcu_r_code[13:0], bmu_dcu_r_code[27:14]}} //nouse
                              : {LINE_LEN_N{bmu_dcu_r_code}}; //nouse

end // g_rfl_code128
 
for (genvar i=0; i<MB_ENT_N; i++) begin : g_mb_ecccode  //为每个Miss Buffer条目生成ECC代码

  assign dff_mb_ecccode_d[i] = evc_wr_mb_bitmap[i] ? evc_line_raw_code : //驱逐写入为真，使用驱逐行的原始ECC代码编码
                                                     refill_line_code  ; //否则使用填充行的ECC编码

  for (genvar j=0; j<WAY_N; j++) begin : g_ecccode
    assign dff_mb_ecccode_en[i][j] = dff_mb_data_en[i][j]; //使能信号，与数据使能同步

    `WDFFER(dff_mb_ecccode_q[i][14*j+:14], dff_mb_ecccode_d[i][14*j+:14], dff_mb_ecccode_en[i][j], clk, rst_n) //位选择，每个way占用14位ECC代码
  end   // g_ecccode

end // g_mb_ecccode

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N*14)) u_aomux_lfl_code (.data_arr(dff_mb_ecccode_q), .sel_in_onehot0(agm_oldest_oh[AGM_LFL]), .data_sel(lfl_code)); //仲裁出一个LFL状态下最老的寄存器保存的code

// lfl_data is already rotated in bmu r-channel response stage.
for (genvar i=0; i<WAY_N; i++) begin : g_lfl_wdata

  // {lfl_code[48:42], lfl_data[223:192], 
  //  lfl_code[34:28], lfl_data[159:128], 
  //  lfl_code[20:14], lfl_data[ 95: 64], 
  //  lfl_code[ 6: 0], lfl_data[ 31:  0]}
  assign lfl_dat_ebank_wdata[i] = {lfl_code[i*2*7 +: 7], lfl_data[i*2*32 +: 32]}; //把miss_buf中的data和code组合写进ebank中

  // {lfl_code[55:49], lfl_data[255:224], 
  //  lfl_code[41:35], lfl_data[191:160], 
  //  lfl_code[27:21], lfl_data[127: 96], 
  //  lfl_code[13: 7], lfl_data[ 63: 32]}
  assign lfl_dat_obank_wdata[i] = {lfl_code[i*2*7+7 +: 7], lfl_data[i*2*32+32 +: 32]}; //把miss_buf中的data和code组合写进obank中
 
end // g_lfl_wdata

assign sbu_dcu_ebank_wdata = {sbu_dcu_data_req_ecc_m1[0], sbu_dcu_data_req_wdata_m1[31: 0]};
assign sbu_dcu_obank_wdata = {sbu_dcu_data_req_ecc_m1[1], sbu_dcu_data_req_wdata_m1[63:32]};

assign sbu_data_req_ebank_wen = {DAT_RAM_D_W{1'b1}};
assign sbu_data_req_obank_wen = {DAT_RAM_D_W{1'b1}};

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N*14)) u_aomux_w_code (.data_arr(dff_mb_ecccode_q), .sel_in_onehot0(agm_oldest_oh[AGM_W]), .data_sel(eb_w_mux_code)); //选出发送给总线的way
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(32)) u_aomux_w_addr (.data_arr(eb_addr), .sel_in_onehot0(agm_oldest_oh[AGM_W]), .data_sel(eb_w_mux_addr)); //选出发送给总线的addr


if (MBUS_W==64) begin : g_eb_code_mbus64
 
assign eb_code = {14{eb_wdata_sel[3]}} & eb_w_mux_code[55:42]   //将校验码格式转成原格式
                 | {14{eb_wdata_sel[2]}} & eb_w_mux_code[41:28]
                 | {14{eb_wdata_sel[1]}} & eb_w_mux_code[27:14]
                 
                 | {14{eb_wdata_sel[0]}} & eb_w_mux_code[13: 0];

end else if (MBUS_W==128) begin : g_eb_code_mbus128

  assign eb_code = {28{eb_wdata_sel[3]}} & {eb_w_mux_code[13: 0], eb_w_mux_code[55:42]}  //nouse
                 | {28{eb_wdata_sel[2]}} & eb_w_mux_code[55:28] //nouse
                 | {28{eb_wdata_sel[1]}} & eb_w_mux_code[41:14] //nouse
                 | {28{eb_wdata_sel[0]}} & eb_w_mux_code[27: 0]; //nouse
 
end // g_eb_code_mbus128

for (genvar i=0; i<MBUS_WORD_N; i++) begin : g_eb_eccdec
 
  assign eb_data_predec[i] = {eb_code[i], eb_wdata[i*32 +: 32]};  //校验码和数据组合进行校验

  wing_cbb_ecc_dec32 u_ecc_dec32_eb_bank (  //对将要写总线的数据进行纠错
    .data_i         ( eb_data_predec [i] ),
    .data_o         ( eb_data_postdec[i] ),
    .syndrome_o     (                    ),
    .error_detected ( eb_data_err[i]     ),
    .single_error_o ( eb_data_sec[i]     ),
    .double_error_o ( eb_data_ded[i]     )
  );

  assign dcu_bmu_w_data[i*32 +: 32] = eb_data_postdec[i][31:0]; //使用纠正后的数据发送给总线
 
end // g_eb_eccdec
 
assign eb_ecc_err = (|eb_data_err) & dcu_bmu_w_vld & csr_ecc_en; //写总线数据发生error
assign eb_ecc_sec = (|eb_data_sec) & dcu_bmu_w_vld & csr_ecc_en; //写总线数据发生1bit error
assign eb_ecc_ded = (|eb_data_ded) & dcu_bmu_w_vld & csr_ecc_en; //写总线数据发生2bit error





//**********************************************************************************************//
//**********************************************************************************************//
`else   // CHUNJUN_SUPPORT_MEM_ECC
//**********************************************************************************************//
//**********************************************************************************************//

assign ram_tag_rdata   = dcu_ram_tag_rdata;   //tag RAM的读取数据
assign ram_data0_rdata = dcu_ram_data0_rdata; //data RAM bank0的读取数据
assign ram_data1_rdata = dcu_ram_data1_rdata; //data RAM bank1的读取数据

assign ram_tag_ecc_sec_m2 = {WAY_N{1'b0}}; //将所有ECC错误信号设置为0（禁用ECC功能）
assign ram_tag_ecc_ded_m2 = {WAY_N{1'b0}}; //将所有ECC错误信号设置为0（禁用ECC功能）
assign tag_ecc_bang_m2    = 1'b0; //禁用ecc使能
assign tag_ecc_sec_m2     = 1'b0; //禁用sec
assign tag_ecc_ded_m2     = 1'b0; //禁用ded
assign ld_vld_m3          = 1'b0; //M3阶段的加载有效信号，设置为0（禁用）
 
assign dcu_lsu_ld_rsp_valid_m3  = 2'b0; //DCU给LSU的M3阶段加载响应有效信号，设置为2'b0（禁用）
assign dcu_lsu_ld_rsp_raserr_m3 = 2'b0; //DCU给LSU的RAS错误信号，设置为2'b0（禁用）
assign dcu_lsu_ld_rsp_replay_m3 = 2'b0; //DCU给LSU的重放请求信号，设置为2'b0（禁用）,m3 replay只有ecc才会。
assign dcu_lsu_ld_rsp_lsqid_m3  = {2*LSQID_W{1'b0}};//DCU给LSU的LSQ ID信号，设置为全0（禁用）

`ifdef CHUNJUN_SUPPORT_V_EXT //只有在定义了CHUNJUN_SUPPORT_V_EXT宏时才编译这部分代码
assign dcu_vpu_ld_rsp_vld_m3    = 2'b0; //DCU给VPU的加载响应有效信号，设置为2'b0（禁用）
assign dcu_vpu_ld_rsp_raserr_m3 = 2'b0; //DCU给VPU的RAS错误信号，设置为2'b0（禁用）
assign dcu_vpu_ld_rsp_replay_m3 = 2'b0; //DCU给VPU的重放请求信号，设置为2'b0（禁用）
assign dcu_vpu_ld_rsp_vlqid_m3  = {2*VLQID_W{1'b0}}; //DCU给VPU的VLQ ID信号，设置为全0（禁用）
`endif  // CHUNJUN_SUPPORT_V_EXT

assign ebank_ecc_ded_m3    = 1'b0; //偶数bank双比特ECC错误信号，设置为0（禁用
assign obank_ecc_ded_m3    = 1'b0; //奇数bank双比特ECC错误信号，设置为0（禁用）
assign ebank_ecc_sec_m3    = 1'b0; //偶数bank单比特ECC错误信号，设置为0（禁用）
assign obank_ecc_sec_m3    = 1'b0; //奇数bank单比特ECC错误信号，设置为0（禁用）
assign ebank_data_m3       = dff_ebank_data_m3_q[31:0]; //偶数bank数据，连接到M3阶段的寄存器输出
assign obank_data_m3       = dff_obank_data_m3_q[31:0]; //奇数bank数据，连接到M3阶段的寄存器输出
assign dff_ebank_data_m3_d = full_data_ebank_m2[31:0];  //偶数bank数据寄存器输入，连接到M2阶段的数据
assign dff_obank_data_m3_d = full_data_obank_m2[31:0];  //奇数bank数据寄存器输入，连接到M2阶段的数据
assign ebank_ecc_pair_m3   = {WAY_N{1'b0}}; //偶数bank ECC配对信号，设置为全0（禁用）
assign obank_ecc_pair_m3   = {WAY_N{1'b0}}; //奇数bank ECC配对信号，设置为全0（禁用）

// 驱逐缓冲区ECC错误信号(禁用ECC功能)
assign eb_ecc_err = 1'b0;  // 驱逐缓冲区ECC错误总信号
assign eb_ecc_sec = 1'b0;  // 驱逐缓冲区单比特ECC错误(可纠正)
assign eb_ecc_ded = 1'b0;  // 驱逐缓冲区双比特ECC错误(不可纠正)

// ECC相关信号(禁用ECC功能)
assign ecc_arb_req       = 1'b0;  // ECC仲裁请求
assign ecc_tag_bank_cs   = {WAY_N{1'b0}};  // ECC标签bank片选
assign ecc_tag_setidx    = {SETIDX_W{1'b0}};  // ECC标签集合索引
assign ecc_tag_wdata     = {WAY_N*TAG_RAM_D_W{1'b0}};  // ECC标签写数据

assign ecc_ebank_cs      = {WAY_N{1'b0}};  // ECC偶数bank片选
assign ecc_obank_cs      = {WAY_N{1'b0}};  // ECC奇数bank片选
assign ecc_ebank_addr    = {32{1'b0}};  // ECC偶数bank地址
assign ecc_obank_addr    = {32{1'b0}};  // ECC奇数bank地址
assign tag_wdata_postecc = tag_wdata_preecc;  // 标签写数据ECC后处理(直接传递)


// lfl_data已经在BMU r通道响应阶段进行了旋转
for (genvar i=0; i<WAY_N; i++) begin : g_lfl_wdata  // 行填充数据写数据生成
  // {lfl_data[223:192], lfl_data[159:128], lfl_data[ 95:64], lfl_data[31: 0]}
  assign lfl_dat_ebank_wdata[i] = lfl_data[i*2*32 +: 32];  // 偶数bank行填充写数据
 
  // {lfl_data[255:224], lfl_data[191:160], lfl_data[127:96], lfl_data[63:32]}
  assign lfl_dat_obank_wdata[i] = lfl_data[i*2*32+32 +: 32];  // 奇数bank行填充写数据
end

// SBU数据请求写数据分配
assign sbu_dcu_ebank_wdata = sbu_dcu_data_req_wdata_m1[31: 0];  // SBU到DCU偶数bank写数据
assign sbu_dcu_obank_wdata = sbu_dcu_data_req_wdata_m1[63:32];  // SBU到DCU奇数bank写数据

for (genvar i=0; i<4; i++) begin : g_sbu_data_req_wen  // SBU数据请求写使能生成
  assign sbu_data_req_ebank_wen[i*8 +: 8] = sbu_dcu_data_req_zero_m1 ? 8'b1111_1111 : {8{sbu_dcu_data_req_wstrb_m1[i]  }};  // SBU数据请求偶数bank写使能
  assign sbu_data_req_obank_wen[i*8 +: 8] = sbu_dcu_data_req_zero_m1 ? 8'b1111_1111 : {8{sbu_dcu_data_req_wstrb_m1[i+4]}};  // SBU数据请求奇数bank写使能
end

assign dcu_bmu_w_data = eb_wdata;  // DCU到BMU的写数据(来自驱逐缓冲区)
assign eb_w_mux_addr  = 32'b0;     // 驱逐缓冲区写多路复用器地址(设为0)


//**********************************************************************************************//
//**********************************************************************************************//
`endif  // CHUNJUN_SUPPORT_MEM_ECC
//**********************************************************************************************//
//**********************************************************************************************//


//=================================================================================
// Interface - BMU AR
//=================================================================================

// 一旦至少有一个条目未完成，我们就向BMU发送ar_vld
assign dcu_bmu_ar_vld  = |agm_query_vld_mask[AGM_AR];  // DCU到BMU的地址读取有效信号

// 来自年龄矩阵的最旧ar条目
wing_cbb_onehot2bin #(.WIDTH(MB_ENT_N)) u_oh2bin_ar (.onehot_i(agm_oldest_oh[AGM_AR]), .bin_o(mb_ar_mux_idx));  // 输入onehot_i = 8'b00010000, 其二进制编号是3'b100

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(32)) u_aomux_ar_addr (.data_arr(dff_mb_addr_q     ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_addr));  // 地址多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(2 )) u_aomux_ar_size (.data_arr(dff_mb_size_q     ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_size));  // 大小多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(3 )) u_aomux_ar_oma  (.data_arr(dff_mb_oma_q      ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_oma ));  // 外部内存属性多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_ar_excl (.data_arr(dff_mb_excl_q     ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_excl));  // 排他多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_ar_priv (.data_arr(dff_mb_priv_q     ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_priv));  // 特权多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_ar_mast (.data_arr(dff_mb_master_q   ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_master));  // 主设备多路复用器
// 只有幂等性、可缓存、带写分配的存储标签检查、带读分配的加载请求需要填充
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_ar_rfl  (.data_arr(dff_mb_is_refill_q), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_is_refill));  // 填充多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_ar_idemp(.data_arr(dff_mb_idemp_q    ), .sel_in_onehot0(agm_oldest_oh[AGM_AR]), .data_sel(mb_ar_mux_idemp    ));  // 幂等性多路复用器

// ARID rules
// 4'b0001:  Exclusive load ID
// 4'b0011:  Device load ID
// 4'b1xxx:  Normal ID

assign dcu_bmu_ar_id = (~mb_ar_mux_is_refill & mb_ar_mux_excl) ? AXI_ID_EXCLD :  //It indicates that this is an exclusive access, default 1
                       (~mb_ar_mux_idemp)                      ? AXI_ID_DVCLD :  //It indicates that this is an device  access, default 3
                         {1'b1, {{4-1-MB_ID_W{1'b0}}, mb_ar_mux_idx }};          //ordinary cache access

assign dff_remap_id_en = dcu_bmu_ar_vld & bmu_dcu_ar_rdy & 
                         ((~mb_ar_mux_is_refill & mb_ar_mux_excl) | ~mb_ar_mux_idemp);
assign dff_remap_id_d  = mb_ar_mux_idx;

`WDFFER(dff_remap_id_q, dff_remap_id_d, dff_remap_id_en, clk, rst_n)

// Address is truncated as per memory-attribute
//  - linefill: aligned to AXI BUS size (64-bit/128-bit)
//  - device: use original instruction request address (LSU not processed)
//  - non-cacheable and no-allocate: use LSU request address
//    - LSU request address offset[1:0] is already set to 2'b00 if this request needs unalign-split.
//      For example, 0xb halfword, is split into two requests, one is 0x8, another one is 0xc.

assign dcu_bmu_ar_addr_offs = mb_ar_mux_is_refill ? {MBUS_SIZE_N{1'b0}} : mb_ar_mux_addr[MBUS_SIZE_N-1:0];
assign dcu_bmu_ar_addr      = { mb_ar_mux_addr[31:MBUS_SIZE_N], dcu_bmu_ar_addr_offs };

// Non-Idempotency(Device) must use original size to send AXI request.
// (Idempotency & non-cacheable) send size 4-Byte since LSU may send unaligned request,
// i.e., offset = 1, size = 2-Byte. (LSU only split request that across 4-Byte boundary)

assign dcu_bmu_ar_size   = ~mb_ar_mux_idemp    ? {1'b0, mb_ar_mux_size} : 
                           mb_ar_mux_is_refill ? MBUS_SIZE_N            :
                                                 3'b010                 ;
assign dcu_bmu_ar_len    = mb_ar_mux_is_refill ? AXI_LINE_LEN_N      : 8'b0;
assign dcu_bmu_ar_burst  = mb_ar_mux_is_refill ? AXI_BURST_WRAP      : AXI_BURST_INCR; //linefill 需要wrap，其他需要incr
assign dcu_bmu_ar_lock   = mb_ar_mux_is_refill ? 1'b0                : mb_ar_mux_excl;
assign dcu_bmu_ar_cache  = ~mb_ar_mux_idemp         ? AXI_CACHE_DEVICE :
                           ~mb_ar_mux_oma[MA_CACHE] ? AXI_CACHE_NC     : 
                                                      {1'b1, mb_ar_mux_oma[MA_RD_ALLOC], 2'b11};
assign dcu_bmu_ar_prot   = {1'b0, 1'b1, mb_ar_mux_priv}; // {instruction/data, security, privileged}
assign dcu_bmu_ar_master = mb_ar_mux_master;

assign dcu_bmu_ar_hdsk = dcu_bmu_ar_vld & bmu_dcu_ar_rdy; // 当AR通道的请求有效且被BMU接收时，设置hdsk信号为1

//=================================================================================
// Interface - BMU AW/W
//=================================================================================

assign dcu_bmu_aw_vld = |agm_query_vld_mask[AGM_AW];
 
// Oldest aw entry from age-matrix.
wing_cbb_onehot2bin #(.WIDTH(MB_ENT_N)) u_oh2bin_aw (.onehot_i(agm_oldest_oh[AGM_AW]), .bin_o(eb_aw_mux_idx)); //将AW通道的id从onehot编码转换为二进制索引

assign dcu_bmu_aw_id   = { {4-MB_ID_W{1'b0}}, eb_aw_mux_idx }; //AW通道的id

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(32)) u_aomux_aw_addr (.data_arr(eb_addr        ), .sel_in_onehot0(agm_oldest_oh[AGM_AW]), .data_sel(eb_aw_mux_addr  ));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_aw_mast (.data_arr(dff_mb_master_q), .sel_in_onehot0(agm_oldest_oh[AGM_AW]), .data_sel(eb_aw_mux_master));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1 )) u_aomux_aw_priv (.data_arr(dff_eb_priv_q  ), .sel_in_onehot0(agm_oldest_oh[AGM_AW]), .data_sel(eb_aw_mux_priv  ));

assign dcu_bmu_aw_addr   = {eb_aw_mux_addr[31:5], 5'b0}; //AW通道的地址，去掉低5位
assign dcu_bmu_aw_size   = MBUS_SIZE_N; //AW通道的size，为4字节
assign dcu_bmu_aw_len    = AXI_LINE_LEN_N ; //AW通道的len，为16字节
assign dcu_bmu_aw_burst  = AXI_BURST_INCR; //AW通道的burst，为增量模式
assign dcu_bmu_aw_lock   = 1'b0; //AW通道的lock，为0
assign dcu_bmu_aw_cache  = 4'b1111; //AW通道的cache，为全1
assign dcu_bmu_aw_prot   = {1'b0, 1'b1, eb_aw_mux_priv}; // {instruction/data, security, privileged}
assign dcu_bmu_aw_atop   = 6'b0; //AW通道的atop，为0
assign dcu_bmu_aw_master = eb_aw_mux_master; //AW通道的master，为eb_aw_mux_master
 
assign dcu_bmu_w_vld = |agm_query_vld_mask[AGM_W]; //W通道的请求有效标志是通过agm_query_vld_mask[AGM_W]来判断的

assign dff_eb_w_cnt_en = dcu_bmu_w_hdsk; //写计数器使能
assign dff_eb_w_cnt_d  = dff_eb_w_cnt_q + 1'b1; //写计数器递增
 
`WDFFER(dff_eb_w_cnt_q, dff_eb_w_cnt_d, dff_eb_w_cnt_en, clk, rst_n) //写数据计数器寄存器

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(WAY_N)) u_aomux_w_way  (.data_arr(dff_mb_victim_way_q), .sel_in_onehot0(agm_oldest_oh[AGM_W]), .data_sel(eb_w_mux_way ));
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(256  )) u_aomux_w_data (.data_arr(dff_mb_data_q      ), .sel_in_onehot0(agm_oldest_oh[AGM_W]), .data_sel(eb_w_mux_data));

// Eviction request read a full cacheline in parallel. 
// Although the cache RAMs are arranged in corkscrew, the eviction data is NOT rotated before sent to BMU.
// When sending w-channel to BMU, 64-bit data is muxed from 256-bit registered line data every beat.
// So we just need to rotate the mux selection input according to the way,
// instead of rotating the full cacheline data, to save the mux area.

if (MBUS_W==64) begin : g_wdata_64
 
  assign eb_wdata_sel = rotate_left_4bit(eb_w_mux_way, dff_eb_w_cnt_q); // 将eb_w_mux_way向左旋转操作
  //表示从miss_buf读出的数据按照原始顺序返回bmu
  assign eb_wdata = {64{eb_wdata_sel[3]}} & eb_w_mux_data[255:192]  // 64{eb_wdata_sel[3]} 表示将eb_wdata_sel[3]扩展为64位，然后与eb_w_mux_data[255:192]进行按位与操作
                  | {64{eb_wdata_sel[2]}} & eb_w_mux_data[191:128] // 64{eb_wdata_sel[2]} 表示将eb_wdata_sel[2]扩展为64位，然后与eb_w_mux_data[191:128]进行按位与操作
                  | {64{eb_wdata_sel[1]}} & eb_w_mux_data[127: 64] // 64{eb_wdata_sel[1]} 表示将eb_wdata_sel[1]扩展为64位，然后与eb_w_mux_data[127: 64]进行按位与操作
                  | {64{eb_wdata_sel[0]}} & eb_w_mux_data[63 :  0]; // 64{eb_wdata_sel[0]} 表示将eb_wdata_sel[0]扩展为64位，然后与eb_w_mux_data[63 :  0]进行按位与操作

end else if (MBUS_W==128) begin : g_wdata_128

  logic [1:0] eb_bank_cnt;
 
  assign eb_bank_cnt = {dff_eb_w_cnt_q, 1'b0}; // 将dff_eb_w_cnt_q的最低位扩展为2位，然后与eb_w_mux_way进行按位与操作
 
  assign eb_wdata_sel = rotate_left_4bit(eb_w_mux_way, eb_bank_cnt); 

  assign eb_wdata = {128{eb_wdata_sel[3]}} & {eb_w_mux_data[63:0], eb_w_mux_data[255:192]} // 128{eb_wdata_sel[3]} 表示将eb_wdata_sel[3]扩展为128位，然后与{eb_w_mux_data[63:0], eb_w_mux_data[255:192]}进行按位与操作
                  | {128{eb_wdata_sel[2]}} & eb_w_mux_data[255:128] // 128{eb_wdata_sel[2]} 表示将eb_wdata_sel[2]扩展为128位，然后与eb_w_mux_data[255:128]进行按位与操作
                  | {128{eb_wdata_sel[1]}} & eb_w_mux_data[191: 64] // 128{eb_wdata_sel[1]} 表示将eb_wdata_sel[1]扩展为128位，然后与eb_w_mux_data[191: 64]进行按位与操作
                  | {128{eb_wdata_sel[0]}} & eb_w_mux_data[127:  0]; // 128{eb_wdata_sel[0]} 表示将eb_wdata_sel[0]扩展为128位，然后与eb_w_mux_data[127:  0]进行按位与操作

end // g_wdata_128


assign dcu_bmu_w_last = (dff_eb_w_cnt_q==AXI_LINE_LEN_N); //W通道的最后一个beat信号
 
assign dcu_bmu_w_strb = {`CHUNJUN_MBUS_DATA_W/8{1'b1}}; //W通道的strb信号总是为1
 
assign dcu_bmu_b_rdy    = 1'b1; //B通道的响应信号总是为1

assign dcu_bmu_aw_hdsk = dcu_bmu_aw_vld & bmu_dcu_aw_rdy; // 当AW通道的请求有效且被BMU接收时，设置hdsk信号为1
assign dcu_bmu_w_hdsk  = dcu_bmu_w_vld  & bmu_dcu_w_rdy ; // 当W通道的请求有效且被BMU接收时，设置hdsk信号为1
assign bmu_dcu_b_hdsk  = bmu_dcu_b_vld  & dcu_bmu_b_rdy ; // 当B通道的请求有效且被BMU接收时，设置hdsk信号为1

//=================================================================================
// Interface - LSU load
//=================================================================================

// 这里ld0_hit_m2不涉及dcu_lsu_ld_replay_cause_m2逻辑，因为MBF/AWL/MHM暗示缺失，而HEB已经从ld0_tag_is_hit中过滤
assign ld0_hit_m2      = ld0_tag_is_hit & ~ld_req_cancel_m2[0];  // 加载0命中M2(标签命中且未取消)
assign ld0_hit_data_m2 = dff_addr_m2_q[2] ? full_data_obank_m2[31:0] : full_data_ebank_m2[31:0];  // 加载0命中数据M2(根据地址选择bank)
// load0 id存储在标签通道流水线寄存器中
assign ld0_hit_id_m2   = dff_tag_id_m2_q;  // 加载0命中ID M2

assign ld1_hit_m2      = (ld0_tag_is_hit & dff_samecl_m2_q |  // 加载1命中M2(加载0命中且相同缓存行，或加载类型且utag命中且未完成驱逐)
                          dff_tag_type_m2_q[TAG_TYPE_LD] & dff_ld1_hit_utag_m2_q & ~ld1_hit_eb_done_m2)
                       & ~ld_req_cancel_m2[1];  // 且未取消
assign ld1_hit_data_m2 = dff_ld1_addr_m2_q[2] ? full_data_obank_m2[31:0] : full_data_ebank_m2[31:0];  // 加载1命中数据M2(根据地址选择bank)
// load1 id存储在数据通道流水线寄存器中
assign ld1_hit_id_m2   = dff_dat_id_m2_q;  // 加载1命中ID M2

// 排他加载ID和设备加载ID是特殊ID，需要重映射到缺失缓冲区ID
assign ld_r_id_remap   = ((bmu_dcu_r_id == AXI_ID_EXCLD) || (bmu_dcu_r_id == AXI_ID_DVCLD)) ? dff_remap_id_q  // 排他加载或设备加载时使用重映射ID
                                                                                            : bmu_dcu_r_id[MB_ID_W-1:0];  // 否则使用BMU响应ID

// 将r通道ID转换为one-hot以选择r计数器使能
wing_cbb_bin2onehot #(.WIDTH(MB_ENT_N)) u_bin2oh_rid (.bin_i(ld_r_id_remap), .onehot_o(bmu_dcu_r_sel));  // 二进制转one-hot

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(LD_OFFS_W )) u_aomux_ldoffs(.data_arr(mb_addr_ldoffs), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_ldoffs));  // 加载偏移多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1         )) u_aomux_rflush(.data_arr(dff_mb_flush_q), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_flushed));  // 刷新多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(LINE_CNT_W)) u_aomux_rcnt  (.data_arr(dff_mb_r_cnt_q), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_cnt    ));  // 计数多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(ID_EXT_W  )) u_aomux_id    (.data_arr(dff_mb_extid_q), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_extid  ));  // ID多路复用器
wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(3       ))u_aomux_rtype (.data_arr(dff_mb_tag_type_q), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_tagtype));  // 标签类型多路复用器

wing_cbb_aomux_idx #(
  .NUM   ( MBUS_WORD_N ),  // 总线字数量
  .WIDTH ( 32          )   // 数据宽度
) u_aomux_ld0data (
  .data_arr ( bmu_dcu_r_data   ),  // 数据数组
  .sel_idx  ( bmu_dcu_r_ldoffs ),  // 选择索引
  .data_sel ( bmu_r_ld0_data   )   // 选择的数据
);

// 当r_cnt==0时，即来自BMU的关键字响应应该发送给load0
assign r_is_critword = ~|bmu_dcu_r_cnt;  // 是关键字的标志

assign bmu_r_ld0_critword  = bmu_dcu_r_vld & bmu_dcu_r_tagtype[TAG_TYPE_LD ] & r_is_critword & ~bmu_dcu_r_flushed;  // BMU加载0关键字响应
assign bmu_r_ld0_ncritword = bmu_dcu_r_vld & bmu_dcu_r_tagtype[TAG_TYPE_LD ] & (~r_is_critword | bmu_dcu_r_flushed);  // BMU加载0非关键字响应
assign bmu_r_stc           = bmu_dcu_r_vld & bmu_dcu_r_tagtype[TAG_TYPE_STC] ;  // BMU存储响应
assign bmu_r_prefetch      = bmu_dcu_r_vld & bmu_dcu_r_tagtype[TAG_TYPE_PRF] ;  // BMU预取响应

assign dcu_bmu_r_rdy    = ld0_rsp_grt_bmu  // DCU到BMU的r通道就绪信号
                        | bmu_r_ld0_ncritword 
                        | bmu_r_stc
                        | bmu_r_prefetch;
 
 assign bmu_dcu_r_hdsk  = bmu_dcu_r_vld  & dcu_bmu_r_rdy ;  // BMU到DCU的r通道握手
 
// 当BMU填充关键字加载响应和流水线M2加载命中响应冲突时，优先选择M2命中，回压BMU
assign ld0_rsp_grt_hit = ld0_hit_m2;  // 加载0响应授予命中
assign ld0_rsp_grt_bmu = bmu_r_ld0_critword & ~ld0_hit_m2;  // 加载0响应授予BMU


// 加载响应接口
 
`ifdef CHUNJUN_SUPPORT_V_EXT  // 支持向量扩展

wing_cbb_aomux #(.NUM(MB_ENT_N), .WIDTH(1)) u_aomux_ldsrc (.data_arr(mb_ldsrc), .sel_in_onehot0(bmu_dcu_r_sel), .data_sel(bmu_dcu_r_ldsrc));  // 加载源多路复用器

assign dcu_vpu_ld_rsp_vld_m2      [0] = ld0_rsp_grt_hit & ~ld_src_m2  // DCU到VPU的加载0响应有效M2(命中且非LSU源)
                                      | ld0_rsp_grt_bmu & ~bmu_dcu_r_flushed & ~bmu_dcu_r_ldsrc;  // 或BMU响应且未刷新且非LSU源

assign dcu_vpu_ld_rsp_data_m2     [0] = {32{ld0_rsp_grt_hit}} & ld0_hit_data_m2 |  // DCU到VPU的加载0响应数据M2
                                        {32{ld0_rsp_grt_bmu}} & bmu_r_ld0_data ;  // 命中数据或BMU数据

assign dcu_vpu_ld_rsp_vlqid_m2    [0] = {VLQID_W{ld0_rsp_grt_hit}} & ld0_hit_id_m2  [VLQID_W-1:0] |  // DCU到VPU的加载0响应VLQID M2
                                        {VLQID_W{ld0_rsp_grt_bmu}} & bmu_dcu_r_extid[VLQID_W-1:0] ;  // 命中ID或BMU扩展ID

assign dcu_vpu_ld_rsp_buserr_m2   [0] = ld0_rsp_grt_bmu & bmu_dcu_r_resp[1];  // DCU到VPU的加载0响应总线错误M2


assign dcu_vpu_ld_rsp_vld_m2      [1] = ld1_hit_m2 & ~ld_src_m2;  // DCU到VPU的加载1响应有效M2(命中且非LSU源)
assign dcu_vpu_ld_rsp_data_m2     [1] = ld1_hit_data_m2;           // DCU到VPU的加载1响应数据M2
assign dcu_vpu_ld_rsp_vlqid_m2    [1] = ld1_hit_id_m2[VLQID_W-1:0];  // DCU到VPU的加载1响应VLQID M2
assign dcu_vpu_ld_rsp_buserr_m2   [1] = 1'b0;                     // DCU到VPU的加载1响应总线错误M2(设为0)

`else   // CHUNJUN_SUPPORT_V_EXT  // 不支持向量扩展
 
assign bmu_dcu_r_ldsrc = 1'b1;  // BMU到DCU的r通道加载源(设为LSU)
 
`endif  // CHUNJUN_SUPPORT_V_EXT


assign dcu_lsu_ld_rsp_valid_m2    [0] = ld0_rsp_grt_hit & ld_src_m2  // DCU到LSU的加载0响应有效M2(命中且LSU源)
                                      | ld0_rsp_grt_bmu & ~bmu_dcu_r_flushed & bmu_dcu_r_ldsrc;  // 或BMU响应且未刷新且LSU源
 
 assign dcu_lsu_ld_rsp_data_m2     [0] = {32{ld0_rsp_grt_hit}} & ld0_hit_data_m2 |  // DCU到LSU的加载0响应数据M2
                                        {32{ld0_rsp_grt_bmu}} & bmu_r_ld0_data ;  // 命中数据或BMU数据
 
 assign dcu_lsu_ld_rsp_lsqid_m2    [0] = {LSQID_W{ld0_rsp_grt_hit}} & ld0_hit_id_m2  [LSQID_W-1:0] |  // DCU到LSU的加载0响应LSQID M2
                                        {LSQID_W{ld0_rsp_grt_bmu}} & bmu_dcu_r_extid[LSQID_W-1:0] ;  // 命中ID或BMU扩展ID

assign dcu_lsu_ld_rsp_buserr_m2   [0] = ld0_rsp_grt_bmu & bmu_dcu_r_resp[1];  // DCU到LSU的加载0响应总线错误M2
assign dcu_lsu_ld_rsp_excl_okay_m2[0] = ld0_rsp_grt_bmu & (bmu_dcu_r_resp == 2'b01);  // DCU到LSU的加载0响应排他成功M2


assign dcu_lsu_ld_rsp_valid_m2    [1] = ld1_hit_m2 & ld_src_m2;  // DCU到LSU的加载1响应有效M2(命中且LSU源)
assign dcu_lsu_ld_rsp_data_m2     [1] = ld1_hit_data_m2;         // DCU到LSU的加载1响应数据M2
assign dcu_lsu_ld_rsp_lsqid_m2    [1] = ld1_hit_id_m2[LSQID_W-1:0];  // DCU到LSU的加载1响应LSQID M2
assign dcu_lsu_ld_rsp_buserr_m2   [1] = 1'b0;                   // DCU到LSU的加载1响应总线错误M2(设为0)
assign dcu_lsu_ld_rsp_excl_okay_m2[1] = 1'b0;                   // DCU到LSU的加载1响应排他成功M2(设为0)

// 如果多个重放原因同时发生，按照MHM > AWL > MBF优先级选择一个
 
assign ld0_replay_cause_m2[REPLAY_MBF] = ld0_real_vld_m2 & tag_raw_miss & mb_full &  // 加载0缺失缓冲区满重放原因
                                         ~all_way_locked & ~hit_mb_m2 ;

assign ld0_replay_cause_m2[REPLAY_AWL] = ld0_real_vld_m2 & tag_raw_miss & all_way_locked &  // 加载0所有路锁定重放原因
                                         ~hit_mb_m2 ;

// REPLAY_MHM不需要检查tag_raw_miss，因为如果hit_mb，m2总是缺失
assign ld0_replay_cause_m2[REPLAY_MHM] = ld0_real_vld_m2 & hit_mb_m2;  // 加载0缺失命中缺失重放原因

// REPLAY_AWU: 如果命中驱逐缓冲区且eb_done，加载0被视为缺失，加载0没有HEB或AWU重放
assign ld0_replay_cause_m2[REPLAY_AWU] = 1'b0;  // 加载0自动唤醒重放原因(设为0)

// Under load1 same cacheline situation:
// - load0 replay, load1 also replay with same cause.
//   - load0 miss and replay with MHM, load1 replay with MHM
//   - load0 miss and replay with MBF, load1 replay with MBF
// - load0 miss (no replay) and enter missbuf, load1 replay with MHM.
//   - This situation include that ld0/ld1 ma is chaned to nc/device, ld0 must be miss, so ld1 replay with MHM.
assign ld1_replay_cause_m2[REPLAY_MBF] = ld1_real_vld_m2 & tag_raw_miss & mb_full & dff_samecl_m2_q &
                                        ~all_way_locked & ~hit_mb_m2 ;

assign ld1_replay_cause_m2[REPLAY_AWL] = ld1_real_vld_m2 & tag_raw_miss & all_way_locked & dff_samecl_m2_q &  // 加载1所有路锁定重放原因
                                        ~hit_mb_m2 ;  // all way locked replay

// 如果hit_mb_m2，加载1重放MHM，无论加载0是否取消
// 但是，如果加载0缺失，加载1仅在加载0未取消时重放MHM
// 否则，当加载0被取消且缺失时，加载1重放AWU
assign ld1_replay_cause_m2[REPLAY_MHM] = ld1_real_vld_m2 & dff_samecl_m2_q &  // 加载1缺失命中缺失重放原因(相同缓存行)
                                         (hit_mb_m2 | ld0_real_vld_m2 & tag_is_miss);  // 且命中缺失缓冲区或加载0有效且标签缺失

assign ld1_replay_cause_m2[REPLAY_AWU] = ld1_real_vld_m2 & (dff_ld1_hit_utag_m2_q & ld1_hit_eb_done_m2      |  // 加载1自动唤醒重放原因(命中utag且驱逐缓冲区完成)
                                                            dff_samecl_m2_q & ~ld0_real_vld_m2 & tag_is_miss);  // 或相同缓存行且加载0无效且标签缺失

assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_MBF] = ld0_replay_cause_m2[REPLAY_MBF] & ld_src_m2;  // DCU到LSU的加载0缺失缓冲区满重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_AWL] = ld0_replay_cause_m2[REPLAY_AWL] & ld_src_m2;  // DCU到LSU的加载0所有路锁定重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_MHM] = ld0_replay_cause_m2[REPLAY_MHM] & ld_src_m2;  // DCU到LSU的加载0缺失命中缺失重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_AWU] = 1'b0;  // DCU到LSU的加载0自动唤醒重放原因(设为0)

assign dcu_lsu_ld_replay_cause_m2[1][REPLAY_MBF] = ld1_replay_cause_m2[REPLAY_MBF] & ld_src_m2;  // DCU到LSU的加载1缺失缓冲区满重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[1][REPLAY_AWL] = ld1_replay_cause_m2[REPLAY_AWL] & ld_src_m2;  // DCU到LSU的加载1所有路锁定重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[1][REPLAY_MHM] = ld1_replay_cause_m2[REPLAY_MHM] & ld_src_m2;  // DCU到LSU的加载1缺失命中缺失重放原因(LSU源)
assign dcu_lsu_ld_replay_cause_m2[1][REPLAY_AWU] = ld1_replay_cause_m2[REPLAY_AWU] & ld_src_m2;  // DCU到LSU的加载1自动唤醒重放原因(LSU源)
 
assign dcu_lsu_ld_replay_lsqid_m2[0] = ld0_hit_id_m2[LSQID_W-1:0];  // DCU到LSU的加载0重放LSQID M2
assign dcu_lsu_ld_replay_lsqid_m2[1] = ld1_hit_id_m2[LSQID_W-1:0];  // DCU到LSU的加载1重放LSQID M2

assign replay_hit_mb_ent = hit_mb_ent_m2 | hit_eb_ent_ndone_m2 ;  // 重放命中缺失缓冲区条目(命中MB或命中EB且未完成)

wing_cbb_lzc #(
    .WIDTH     ( MB_ENT_N ),  // 缺失缓冲区条目数量
    .DIRECTION ( 0 ),   // DIRECTION = 0: 尾随零计数
    .INVERT    ( 0 )
) u_lzc_hit_mb (
    .vec_i   ( replay_hit_mb_ent ),  // 输入向量
    .cnt_o   ( replay_hit_mb_id  ),  // 输出计数
    .empty_o (                   )   // 空标志
);
 
assign dcu_lsu_ld_replay_mbid_m2[0] = replay_hit_mb_id;  // DCU到LSU的ld0重放缺失缓冲区ID

// 当加载1相同缓存行且加载0重放时，加载1也以相同原因和相同mbid重放
// 否则，当前加载0的push_idx是加载1的MHM mbid
assign dcu_lsu_ld_replay_mbid_m2[1] = hit_mb_m2 ? replay_hit_mb_id :  // DCU到LSU的加载1重放缺失缓冲区ID(命中MB时使用重放命中ID)
                                                  mb_push_idx      ;  // 否则使用推送索引

assign dcu_lsu_mb_rls_array  = dff_mb_rls_array_q;


`ifdef CHUNJUN_SUPPORT_V_EXT

assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_MBF] = ld0_replay_cause_m2[REPLAY_MBF] & ~ld_src_m2; //Load0的Miss Buffer Full重放原因，条件是VPU来源（~ld_src_m2）
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_AWL] = ld0_replay_cause_m2[REPLAY_AWL] & ~ld_src_m2; //Load0的All Way Lock重放原因，条件是VPU来源
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_MHM] = ld0_replay_cause_m2[REPLAY_MHM] & ~ld_src_m2; //Load0的Miss Hit Miss重放原因，条件是VPU来源
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_AWU] = 1'b0; //Load0的Auto Wake Up重放原因，条件是VPU来源

assign dcu_vpu_ld_replay_cause_m2[1][REPLAY_MBF] = ld1_replay_cause_m2[REPLAY_MBF] & ~ld_src_m2; //Load1的Miss Buffer Full重放原因，条件是VPU来源（~ld_src_m2）
assign dcu_vpu_ld_replay_cause_m2[1][REPLAY_AWL] = ld1_replay_cause_m2[REPLAY_AWL] & ~ld_src_m2; //Load1的All Way Lock重放原因，条件是VPU来源
assign dcu_vpu_ld_replay_cause_m2[1][REPLAY_MHM] = ld1_replay_cause_m2[REPLAY_MHM] & ~ld_src_m2; //Load1的Miss Hit Miss重放原因，条件是VPU来源
assign dcu_vpu_ld_replay_cause_m2[1][REPLAY_AWU] = ld1_replay_cause_m2[REPLAY_AWU] & ~ld_src_m2; //Load1的Auto Wake Up重放原因，条件是VPU来源
 
assign dcu_vpu_ld_replay_vlqid_m2[0] = ld0_hit_id_m2[VLQID_W-1:0]; //Load1对应的VPU加载队列ID
assign dcu_vpu_ld_replay_vlqid_m2[1] = ld1_hit_id_m2[VLQID_W-1:0]; //Load1对应的VPU加载队列ID
 
assign dcu_vpu_ld_replay_mbid_m2 [0] = replay_hit_mb_id; //Load0的Miss Buffer ID，使用重放命中的Miss Buffer ID
assign dcu_vpu_ld_replay_mbid_m2 [1] = hit_mb_m2 ? replay_hit_mb_id :  // Load1的Miss Buffer ID，如果命中Miss Buffer则使用重放命中的ID，否则使用推送索引
                                                   mb_push_idx      ;
 
assign dcu_vpu_mb_rls_array          = dff_mb_rls_array_q; //将Miss Buffer释放数组直接传递给VPU，用于VPU了解哪些Miss Buffer条目已释放
 
`endif // CHUNJUN_SUPPORT_V_EXT


//=================================================================================
// Interface - SBU tag check
//=================================================================================

// DCU到SBU的标签响应信号配置
assign dcu_sbu_tag_rsp_vld_m2      = dff_tag_type_m2_q[TAG_TYPE_STC];  // 存储类型标签响应有效信号
// 这里不涉及dcu_sbu_tag_replay_cause_m2逻辑，因为MBF/AWL/MHM暗示缺失，而HEB已经从stc_tag_is_hit中过滤
assign dcu_sbu_tag_rsp_hit_m2      = stc_tag_is_hit;                   // 存储标签命中信号
assign dcu_sbu_tag_rsp_status_m2   = tag_hit_dirty_bit;                // 存储标签dirty状态
assign dcu_sbu_tag_rsp_sbqid_m2    = dff_tag_id_m2_q[SBQID_W-1:0];    // 存储缓冲区队列ID
assign dcu_sbu_tag_rsp_excl_ok_m2  = 1'b0;                             // 排他操作成功信号(存储操作不使用)
assign dcu_sbu_tag_rsp_raserr_m2   = tag_ecc_ded_m2;                   // 标签RAS的两bit错误信号
 
// 标签响应路选择信号
assign dcu_sbu_tag_rsp_way_oh = dcu_sbu_tag_rsp_hit_m2 ? tag_way_cmp : victim_way;  // 命中时选择命中路，否则选择牺牲路

wing_cbb_onehot2bin #(.WIDTH(WAY_N)) u_oh2binmbid (.onehot_i(dcu_sbu_tag_rsp_way_oh), .bin_o(dcu_sbu_tag_rsp_way_m2));  // 路选择one-hot转二进制

// DCU到SBU的标签重放原因信号配置
assign dcu_sbu_tag_replay_cause_m2[REPLAY_MBF] = dff_tag_type_m2_q[TAG_TYPE_STC] & tag_raw_miss & real_ima_m2[MA_WR_ALLOC] & mb_full &  // 缺失缓冲区满重放
                                                 ~all_way_locked & ~hit_mb_m2;

assign dcu_sbu_tag_replay_cause_m2[REPLAY_AWL] = dff_tag_type_m2_q[TAG_TYPE_STC] & tag_raw_miss & real_ima_m2[MA_WR_ALLOC] & all_way_locked &  // 所有路锁定重放
                                                 ~hit_mb_m2;

// REPLAY_MHM不需要检查tag_raw_miss，因为如果hit_mb，m2总是缺失
assign dcu_sbu_tag_replay_cause_m2[REPLAY_MHM] = dff_tag_type_m2_q[TAG_TYPE_STC] & hit_mb_m2;  // 缺失命中缺失重放

// REPLAY_HEB不需要检查tag_raw_miss，因为如果hit_eb，m2总是命中，因为我们没有使驱逐的缓存行标签无效
assign dcu_sbu_tag_replay_cause_m2[REPLAY_HEB] = dff_tag_type_m2_q[TAG_TYPE_STC] & hit_eb_as_replay_m2;  // 命中驱逐缓冲区重放

assign dcu_sbu_tag_replay_cause_m2[REPLAY_TCE] = dff_tag_type_m2_q[TAG_TYPE_STC] & tag_ecc_sec_m2 &  // 标签检查ECC错误重放
                                                 ~dcu_sbu_tag_replay_cause_m2[REPLAY_MBF]         &
                                                 ~dcu_sbu_tag_replay_cause_m2[REPLAY_AWL]         &
                                                 ~hit_mb_m2                                       &
                                                 ~hit_eb_as_replay_m2                             ;

assign dcu_sbu_tag_replay_mbid_m2 = replay_hit_mb_id;  // 重放命中缺失缓冲区ID
 
// 仅过滤SBU标签检查缺失的行填充握手
assign dcu_sbu_filldone       = lfl_wr_vld & lfl_wr_rdy & lfl_tag_type[TAG_TYPE_STC];  // 行填充完成信号
assign dcu_sbu_filldone_err   = lfl_err;                                                // 行填充错误信号
assign dcu_sbu_filldone_sbqid = lfl_extid[SBQID_W-1:0];                                // 行填充存储缓冲区队列ID

assign dcu_sbu_mb_rls_array  = dff_mb_rls_array_q;   // 缺失缓冲区释放数组
assign dcu_sbu_eb_done_array = dff_eb_rls_array_q;   // 驱逐缓冲区完成数组


//=================================================================================
// Interface - SBU data rd/wr
//=================================================================================

// DCU到SBU的数据响应信号配置
assign dcu_sbu_data_rsp_vld_m3    = dff_std_vld_m3_q;  // 存储数据响应有效信号
assign dcu_sbu_data_rsp_rdata_m3  = {obank_data_m3[31:0], ebank_data_m3[31:0]};  // 将数据组合返回SBU
assign dcu_sbu_data_rsp_raserr_m3 = data_ecc_ded_m3;  // 返回数据RAS错误信号
assign dcu_sbu_data_rsp_sbqid_m3  = dff_dat_id_m3_q[SBQID_W-1:0];  // 存储请求的ID


//=================================================================================
// Invalidate All DCache
//=================================================================================

// There are two cases whick need to invalidate all DCache
//  - Initialize DCache RAM to all zero.
//  - Invalidate control CSR

// Initialize statue register
assign dff_init_done_en = ~dff_init_done_q & cnt_invld_reached;
assign dff_init_done_d  = 1'b1;

`WDFFER(dff_init_done_q, dff_init_done_d, dff_init_done_en, clk, rst_n) // 初始化完成标志
 
assign dcu_init_busy = ~dff_init_done_q; // 初始化未完成

assign invld_raw_req = ~dff_init_done_q | calm_state_inval; // 初始化未完成或者calm_state_inval为1时，开始进行无效化操作
// invld_arb_req is used to generate RAM control signal,
// cmo is not started untill pipeline is idle, even cmo get arbiter granted.
assign invld_arb_req = invld_raw_req & pipeline_idle; // 当pipeline空闲时，同时invld请求时，开始进行无效化操作
 
// Invalidate counter for each cacheline set.
// When all four 64-bit sections of one data RAM cacheline are written to zero,
// the dff_invld_cnt_q will increase one.
// Arbiter is not considered here since cache maintainance operation is always highest priority.

assign dff_invld_cnt_en = invld_arb_req & (&dff_invld_cnt_offs_q);  // 当dff_invld_cnt_offs_q == 1时，计set一次
assign dff_invld_cnt_d  = dff_invld_cnt_q + 1'b1; //[SETIDX_W-1:0]（9位，对应512个cache set）
 
`WDFFER(dff_invld_cnt_q, dff_invld_cnt_d, dff_invld_cnt_en, clk, rst_n)  //[SETIDX_W-1:0]（9位，对应512个cache set）

// 2-bit counter for each 64-bit section of one data RAM cacheline.
assign dff_invld_cnt_offs_en = invld_arb_req; // 当invld请求时，开始进行无效化操作
assign dff_invld_cnt_offs_d  = dff_invld_cnt_offs_q + 1'b1; // //[1:0]（2位，对应4个64位段）

`WDFFER(dff_invld_cnt_offs_q, dff_invld_cnt_offs_d, dff_invld_cnt_offs_en, clk, rst_n) //[1:0]（2位，对应4个64位段）
 
assign cnt_invld_reached = (&dff_invld_cnt_q) & (&dff_invld_cnt_offs_q); //完成invld 所有cacheline


//=================================================================================
// CBOM (Cache-Block Management)
// invalid, clean, flush
//=================================================================================

// CBOM状态机状态定义
localparam IDLE  = 5'b00000;  // 空闲状态
localparam WAIT  = 5'b00001;  // 等待状态(等待流水线空闲)
localparam CHK   = 5'b00010;  // 检查状态(检查标签命中)
localparam INVAL = 5'b00100;  // 无效化状态(清除valid位)
localparam CLEAN = 5'b01000;  // 清理状态(写回dirty数据)
localparam ECC   = 5'b10000;  // ECC错误状态(处理ECC错误)
 
// CALM状态机状态定义
localparam IDLE_ALL  = 2'b00;  // 空闲状态
localparam CLEAN_ALL = 2'b01;  // 清理所有状态
localparam INVAL_ALL = 2'b10;  // 无效化所有状态
 
// CBOM操作类型定义
localparam CBOM_INVAL = 2'b01;  // 无效化操作(清除valid位)
localparam CBOM_CLEAN = 2'b10;  // 清理操作(写回dirty数据)
localparam CBOM_FLUSH = 2'b11;  // 刷新操作(写回dirty数据后清除valid位)

// CBOM基于地址匹配
// 只有匹配的缓存行才会执行无效化/清理/刷新操作
// 因此在相应操作前需要进行标签命中检查

// CBOM状态机
//
//          <--ECC<--->-------------->
//          |       |                |
// IDLE -> WAIT -> CHK -> CLEAN -- INVAL
//                  |                |
//          ^--------                |
//          ^-------------------------
//
// CALM(缓存全管理)复用CBOM状态机来实现清理操作
//  - 在CALM检查缓存行非valid-dirty时，添加CHK状态到WAIT状态的转换
//  - 在缓存行驱逐完成时，添加INVAL状态到WAIT状态的转换

// CBOM请求信号：SBU标签请求有效且为CBOM操作(非CALM且操作码非0)
assign sbu_dcu_cbom_req = sbu_dcu_tag_req_vld_m1 & ~sbu_dcu_tag_req_cmoop_m1[2] & (|sbu_dcu_tag_req_cmoop_m1[1:0]);

// CBOM操作使能：CBOM请求且状态机空闲，或CALM请求且CALM状态机空闲
assign dff_cbom_op_en = (sbu_dcu_cbom_req & cbom_state_idle) | 
                        (sbu_dcu_calm_req & calm_state_idle) ;
assign dff_cbom_op_d  = sbu_dcu_tag_req_cmoop_m1[1:0];  // CBOM操作类型

`WDFFER(dff_cbom_op_q, dff_cbom_op_d, dff_cbom_op_en, clk, rst_n)  // CBOM操作类型寄存器
 
// CBOM操作类型解码
assign cbom_op_inval = (dff_cbom_op_q == CBOM_INVAL);  // CBOM无效化操作
assign cbom_op_clean = (dff_cbom_op_q == CBOM_CLEAN);  // CBOM清理操作
assign cbom_op_flush = (dff_cbom_op_q == CBOM_FLUSH);  // CBOM刷新操作

// CBOM状态机组合逻辑
always_comb begin : g_cbom_state
  dff_cbom_state_d = dff_cbom_state_q;

  unique case (dff_cbom_state_q)
    IDLE: begin 
       // 由于sbu_dcu_calm_clean包括clean和flush，所以都需要读取tag，所以复用WAIT逻辑去使能tag_ram，而calm invld直接向tag RAM写入全0数据，不需要读取tag
      dff_cbom_state_d = (sbu_dcu_cbom_req | sbu_dcu_calm_clean) ? WAIT  // CBOM请求或CALM清理请求时转到WAIT
                                                                 : IDLE;   // 否则保持IDLE
    end
    // 在此状态访问tag_ram，读取tag数据
    WAIT: begin
      dff_cbom_state_d = pipeline_idle ? CHK  // 等待流水线空闲后，开始检查tag
                                       : WAIT; // 否则保持WAIT
    end
    CHK : begin
      dff_cbom_state_d = tag_ecc_sec_m2   ? ECC :  // 如果tag RAM发生ECC错误，则进入ECC状态
                         calm_state_clean ? ((calm_mux_tag_vld_dirty & ~tag_ecc_ded_m2) ? CLEAN :  // CALM: 如果当前way是valid+dirty且没有ECC错误，转到CLEAN
                                             calm_clean_reached                         ? IDLE  :  // CALM: 如果所有way都处理完，转到IDLE
                                                                                          WAIT  ) :  // CALM: 如果当前way不是valid+dirty，转到WAIT
                         tag_raw_hit      ? ( ((cbom_op_flush | cbom_op_clean) & tag_hit_dirty_bit & ~tag_ecc_ded_m2) ? CLEAN :  // CBOM: 如果当前way是valid+dirty且没有ECC错误，转到CLEAN
                                              (cbom_op_flush | cbom_op_inval) ? INVAL :  // CBOM: 如果当前way是valid+dirty且没有ECC错误，转到INVAL
                                                                                IDLE  ) :  // CBOM: 如果当前way不是valid+dirty，转到IDLE
                                            IDLE;
    end
    CLEAN: begin
      dff_cbom_state_d = (|mb_pop_cbom) ? INVAL  // CLEAN: 将dirty cacheline的数据写回内存后转到INVAL
                                        : CLEAN;  // 否则保持CLEAN
    end
    INVAL: begin
      // 当clean指令来临时，需要清理所有cacheline的dirty位，所以需要复用逻辑
      dff_cbom_state_d = (calm_state_clean & ~calm_clean_reached) ? WAIT : IDLE;  // 更新tag RAM，清除dirty位, 等待下一个cacheline， 如果不是clean，那么清除valid位，等待下一个cacheline
    end
    ECC : begin
      dff_cbom_state_d = WAIT;  // ECC错误处理后转到WAIT
    end
    default: dff_cbom_state_d = dff_cbom_state_q;
  endcase
end

`WDFFR(dff_cbom_state_q, dff_cbom_state_d, clk, rst_n)

// CBOM状态机状态信号解码
assign cbom_raw_req       = |dff_cbom_state_q;  // CBOM原始请求信号(状态机非空闲)
assign cbom_state_idle    = (dff_cbom_state_q == IDLE  );  // CBOM空闲状态
assign cbom_state_rdtag   = (dff_cbom_state_q == WAIT  ) & pipeline_idle;  // CBOM读取标签状态(WAIT且流水线空闲)
assign cbom_state_into_eb = (dff_cbom_state_q == CHK   ) & (cbom_op_flush | cbom_op_clean) & ~tag_ecc_bang_m2 &  // CBOM进入驱逐缓冲区状态
                            (~calm_state_clean & tag_raw_hit & tag_hit_dirty_bit |  // CBOM: 标签命中且dirty
                              calm_state_clean & calm_mux_tag_vld_dirty          );  // CALM: 标签valid+dirty
assign cbom_state_chk     = (dff_cbom_state_q == CHK   );  // CBOM检查状态
assign cbom_state_clean   = (dff_cbom_state_q == CLEAN );  // CBOM清理状态
assign cbom_state_inval   = (dff_cbom_state_q == INVAL );  // CBOM无效化状态


assign dff_cbom_addr_en = sbu_dcu_cbom_req & cbom_state_idle;
assign dff_cbom_addr_d  = sbu_dcu_tag_req_addr_m1;
 
`WDFFER(dff_cbom_addr_q, dff_cbom_addr_d, dff_cbom_addr_en, clk, rst_n)

// Master bit is used for both cbom and calm, so enable signal is relaxed.
assign dff_cbom_master_en = stc_cmo_grt;
assign dff_cbom_master_d  = sbu_dcu_tag_req_master_m1;

`WDFFER(dff_cbom_master_q, dff_cbom_master_d, dff_cbom_master_en, clk, rst_n)
 
assign dff_cbom_way_en = cbom_state_chk; //cbom状态机处于检查状态时，更新victim_way中的hit way
assign dff_cbom_way_d  = victim_way; //victim_way中的hit way

`WDFFER(dff_cbom_way_q, dff_cbom_way_d, dff_cbom_way_en, clk, rst_n) //dff_cbom_way_q, 来自victim_way中的hit way
 
assign dff_cbom_tag_en = cbom_state_chk;      //cbom状态机处于检查状态时，更新victim_way中的hit way
assign dff_cbom_tag_d  = victim_way_tag_bits; //victim_way中的hit way的tag数据

`WDFFER(dff_cbom_tag_q, dff_cbom_tag_d, dff_cbom_tag_en, clk, rst_n) //dff_cbom_tag_q, 来自victim_way中的hit way的tag数据

// If ecc error occured when evicting, record the error and invalidate correspoding cacheline in later INVAL stage.
assign dff_cbom_clean_err_set = cbom_state_clean & eb_ecc_err;
assign dff_cbom_clean_err_clr = cbom_state_inval;
assign dff_cbom_clean_err_en  = dff_cbom_clean_err_set | dff_cbom_clean_err_clr;
assign dff_cbom_clean_err_d   = dff_cbom_clean_err_set;

`WDFFER(dff_cbom_clean_err_q, dff_cbom_clean_err_d, dff_cbom_clean_err_en, clk, rst_n)

assign cbom_addr_m1 = calm_state_clean ? { {TAG_W{1'b0}}, dff_calm_cl_cnt_q, 5'b0} : dff_cbom_addr_q; //calm状态机处于清理状态时，所有set都清零, 否则使用cbom_addr_q清除指定的地址

always_comb begin : g_cbom_ctrl
  cbom_arb_req     = 1'b0;
  cbom_tag_bank_cs = 4'b0000;
  cbom_tag_setidx  = cbom_addr_m1[5+:SETIDX_W];
  cbom_tag_wr      = 1'b0;
  cbom_tag_wdata   = {TAG_FULL_W{1'b0}};

  unique case (dff_cbom_state_q)
    WAIT: begin
      if (pipeline_idle) begin 
        cbom_arb_req     = 1'b1;    // 请求仲裁器
        cbom_tag_bank_cs = calm_state_clean ? calm_clean_way : 4'b1111; // 清理时，使用set计数器的经过独热码的way，其他情况读取way判断命中
      end
    end
    INVAL: begin
      // cbom clean write tag clean; cbom flush/invalid write tag all zero
      cbom_arb_req     = 1'b1;
      cbom_tag_bank_cs = dff_cbom_way_q; // 选择要cbom操作的way, victim_way中的hit way
      cbom_tag_wr      = 1'b1;
      cbom_tag_wdata   = (cbom_op_clean & ~dff_cbom_clean_err_q) //cbom clean操作，且没有ECC错误
                         ?  {1'b0, 1'b1, dff_cbom_tag_q}   // 更新的读出的dirty, valid, tag
                         :  {TAG_FULL_W{1'b0}}; // 如果不是cbom clean操作或者有ECC错误，则写入全0数据
    end
    default: cbom_tag_bank_cs = 4'b0000;
  endcase
end

assign dcu_sbu_cmo_done = calm_state_idle & cbom_raw_req & (dff_cbom_state_d == IDLE) |
                          calm_state_busy & (dff_calm_state_d == IDLE_ALL);


//=================================================================================
// CSR CALM (Cache-All Management)
// invalid all, clean all, flush all
//=================================================================================

// CALM is for all dcache cacheline.
// Every cacheline is iterated to do invalid/clean/flush operation.
 
// CALM state-machine
//
//             ------------------------------>
//             |                             |          
// IDLE -> waiting -> clean-all -> invalidate-all
//                                           |
//   ^----------------------------------------

assign sbu_dcu_calm_req   = sbu_dcu_tag_req_vld_m1 & sbu_dcu_tag_req_cmoop_m1[2] & (|sbu_dcu_tag_req_cmoop_m1[1:0]); //SBU的tag请求有效， CMO操作的第2位为1（表示这是一个CMO操作），第1位或第0位至少有一个为1（表示具体的CMO类型） 
assign sbu_dcu_calm_clean = sbu_dcu_tag_req_vld_m1 & sbu_dcu_tag_req_cmoop_m1[2] &  //SBU的tag请求有效， CMO操作的第2位为1（表示这是一个CMO操作）
                            ((sbu_dcu_tag_req_cmoop_m1[1:0] == CBOM_CLEAN) | (sbu_dcu_tag_req_cmoop_m1[1:0] == CBOM_FLUSH)); //这是一个需要清理cache的CMO请求(clean/flush)

always_comb begin : g_calm_state //calm状态机
  dff_calm_state_d = dff_calm_state_q;

  unique case (dff_calm_state_d)
    IDLE_ALL: begin
      dff_calm_state_d = sbu_dcu_calm_clean ? CLEAN_ALL : // 如果calm_clean请求，则进入CLEAN_ALL状态
                         sbu_dcu_calm_req   ? INVAL_ALL : // 如果除了clean的其他请求请求（flush/invalid），则进入INVAL_ALL状态
                                              IDLE_ALL  ; // 其他情况保持IDLE_ALL状态
    end
    CLEAN_ALL: begin
      // calm op reuse cbom op
      dff_calm_state_d = calm_clean_reached ? (cbom_op_flush ? INVAL_ALL : IDLE_ALL) //是否清理完成？，清理完成后，还需要使所有cacheline无效？
                                            : CLEAN_ALL;
    end
    INVAL_ALL: begin
      dff_calm_state_d = cnt_invld_reached ? IDLE_ALL //是否无效化完成？
                                           : INVAL_ALL;
    end
    default: dff_calm_state_d = dff_calm_state_q;
  endcase
end

`WDFFR(dff_calm_state_q, dff_calm_state_d, clk, rst_n)

assign calm_state_idle  = (dff_calm_state_q == IDLE_ALL ); //calm状态机处于空闲状态
assign calm_state_busy  = ~calm_state_idle; //calm状态机处于忙碌状态
assign calm_state_clean = (dff_calm_state_q == CLEAN_ALL); //calm状态机处于清理状态
assign calm_state_inval = (dff_calm_state_q == INVAL_ALL); //calm状态机处于无效化状态


// calm clean cacheline counter
assign dff_calm_cl_cnt_en = calm_clean_incr & (&dff_calm_way_cnt_q); //cacheline cnt, 当一个cacheline的所有way都处理完后，移动到下一个cacheline
assign dff_calm_cl_cnt_d  = dff_calm_cl_cnt_q + 1'b1;

`WDFFER(dff_calm_cl_cnt_q, dff_calm_cl_cnt_d, dff_calm_cl_cnt_en, clk, rst_n)

// calm clean way coutner for each cacheline
assign dff_calm_way_cnt_en = calm_clean_incr;
assign dff_calm_way_cnt_d  = dff_calm_way_cnt_q + 1'b1; //way 计数器，遍历当前cacheline的每个way（0, 1, 2, 3）

`WDFFER(dff_calm_way_cnt_q, dff_calm_way_cnt_d, dff_calm_way_cnt_en, clk, rst_n) //遍历当前cacheline的每个way（0, 1, 2, 3）

wing_cbb_bin2onehot #(.WIDTH(WAY_N)) u_bin2oh_calm_clean (.bin_i(dff_calm_way_cnt_q), .onehot_o(calm_clean_way)); //将way计数器转换为onehot编码，作为way的选择
//根据当前way计数器选择对应way的tag数据
wing_cbb_aomux_idx #(.NUM(WAY_N), .WIDTH(1)) u_aomux_calm_vld (.data_arr(tag_rdata_valid_bit), .sel_idx(dff_calm_way_cnt_q), .data_sel(calm_mux_tag_vld));
wing_cbb_aomux_idx #(.NUM(WAY_N), .WIDTH(1)) u_aomux_calm_dty (.data_arr(tag_rdata_dirty_bit), .sel_idx(dff_calm_way_cnt_q), .data_sel(calm_mux_tag_dirty));
//检查当前way是否既是valid又是dirty,只有valid且dirty的cacheline才需要清理
assign calm_mux_tag_vld_dirty = calm_mux_tag_vld & calm_mux_tag_dirty;

assign calm_clean_incr = calm_state_clean & //CALM处于清理状态
                         (cbom_state_chk   & ~tag_ecc_sec_m2 & ~calm_mux_tag_vld_dirty | //CBOM处于检查状态 ，且不是ecc错误，且不是valid且dirty的cacheline
                          cbom_state_inval ); //CBOM处于无效化状态

assign calm_clean_reached = calm_clean_incr & (&dff_calm_cl_cnt_q) & (&dff_calm_way_cnt_q); //可以递增 &&  cacheline计数器达到最大 && way计数器达到最大


//=================================================================================
// CMO to interface
//=================================================================================

// cmo_raw_req is used for LSU/SBU interface and set high once received maintenance operation,
// so LSU/SBU requests are back-pressed by cmo_raw_req.
assign cmo_raw_req = invld_raw_req  |  // 无效化原始请求
                     ecc_arb_req    |  // ECC仲裁请求
                     cbom_raw_req   ;  // 缓存块操作原始请求

// cmo_arb_req is used for main arbiter and set high only when neccesarily access cache RAM,
// so linefill and evict rd can still get arbiter grant when cmo waiting pipeline_idle.
// cmo_arb_req用于主仲裁器，只有在必要时访问缓存RAM时才置高，
// 因此当CMO等待流水线空闲时，行填充和逐出读取仍可获得仲裁器授权。
assign cmo_arb_req = invld_arb_req |   // 无效化仲裁请求
                     ecc_arb_req   |   // ECC仲裁请求
                     cbom_arb_req  ;   // 缓存块操作仲裁请求

// CMO to RAM interface

assign cmo_tag_bank_cs  = ecc_arb_req  ? ecc_tag_bank_cs  :    // ECC仲裁请求时，使用ECC仲裁器选择的bank
                          cbom_arb_req ? cbom_tag_bank_cs :    // 缓存块操作仲裁请求时，使用缓存块操作仲裁器选择的bank
                                         {WAY_N{invld_arb_req & (~|dff_invld_cnt_offs_q)}}; // 无效化仲裁请求时，使用无效化仲裁器选择的bank

assign cmo_tag_wr       = ecc_arb_req  ? 1'b1   :     // ECC仲裁请求时，写入tag RAM
                          cbom_arb_req ? cbom_tag_wr :// 缓存块操作仲裁请求时，写入tag RAM
                                         1'b1   ;     // 无效化仲裁请求时，写入tag RAM

assign cmo_tag_setidx   = ecc_arb_req  ? ecc_tag_setidx   :  // ECC仲裁请求时，使用ECC仲裁器选择的set
                          cbom_arb_req ? cbom_tag_setidx  :  // 缓存块操作仲裁请求时，使用缓存块操作仲裁器选择的set
                                         dff_invld_cnt_q  ;  // 无效化仲裁请求时，使用无效化仲裁器选择的set

// ecc_tag_wdata is not involved in cmo_tag_wdata since ecc_tag_wdata already have ecc encode,
// however cmo_tag_wdata will do ECC encode separately.
assign cmo_tag_wdata    = cbom_arb_req ? cbom_tag_wdata  :            // 缓存块操作仲裁请求时，使用缓存块操作仲裁器选择的wdata
                                         {TAG_FULL_W{1'b0}};          // 无效化仲裁请求时，使用数据0作为wdata


assign cmo_dat_ebank_cs  = ecc_arb_req ? ecc_ebank_cs : {WAY_N{invld_arb_req}}; //ebank发生错误时，精确选择需要修正的way ， 无效化时：（所有way都被选中）
assign cmo_dat_obank_cs  = ecc_arb_req ? ecc_obank_cs : {WAY_N{invld_arb_req}}; //obank发生错误时，精确选择需要修正的way ， 无效化时：（所有way都被选中）
 
assign cmo_dat_wr           = 1'b1; // 写入数据RAM
assign cmo_dat_ebank_addr   = ecc_arb_req ? ecc_ebank_addr[3+:DAT_RAM_A_W] 
                                          : {dff_invld_cnt_q, dff_invld_cnt_offs_q};
assign cmo_dat_obank_addr   = ecc_arb_req ? ecc_obank_addr[3+:DAT_RAM_A_W] 
                                          : {dff_invld_cnt_q, dff_invld_cnt_offs_q};
assign cmo_dat_ebank_wdata  = ecc_arb_req ? ebank_data_m3 : {DAT_RAM_D_W{1'b0}}; //ebank出现错误后纠正的数据
assign cmo_dat_obank_wdata  = ecc_arb_req ? obank_data_m3 : {DAT_RAM_D_W{1'b0}}; //obank出现错误后纠正的数据
assign cmo_dat_wen          = {DAT_RAM_D_W{1'b1}}; // 纠正数据都是写入操作，所以为1


//=================================================================================
// Control Interface
//=================================================================================

assign cmo_idle       = ~cmo_raw_req;
assign pipeline_idle  = mb_empty & (~|dff_tag_type_m2_q) & (~|dff_dat_type_m2_q) & ~dff_std_vld_m3_q & ~ecc_arb_req & ~ld_vld_m3;
assign dcu_idle       = pipeline_idle & cmo_idle;

assign csr_dcache_en      = pcu_csr_msyscfg[1]; // 通过CSR寄存器 控制DCACHE使能
assign csr_ecc_en         = pcu_csr_msyscfg[3]; // 通过CSR寄存器 控制ECC使能
assign csr_inject_en      = pcu_csr_meictl[0]; // 通过CSR寄存器 控制ECC注入使能
assign csr_inject_dest    = pcu_csr_meictl[4:1]; // 通过CSR寄存器 控制ECC注入目标
assign csr_inject_bank    = pcu_csr_meictl[8:5]; // 通过CSR寄存器 控制ECC注入bank
assign csr_inject_bitmapl = pcu_csr_meibmap[31:0]; // 通过CSR寄存器 控制ECC注入bitmap低32位
assign csr_inject_bitmaph = pcu_csr_meibmaph[31:0]; // 通过CSR寄存器 控制ECC注入bitmap高32位

// Cache L1 rw access:
//  - LSU and DCU handshake
//  - LSU and SBU tag handshake
// PMU性能监控访问信号配置
assign dcu_pmu_access[0] = lsu_dcu_ld_req_valid_m1[0] & dcu_lsu_ld_req_ready_m1[0] |  // 端口0加载请求握手成功
                           sbu_dcu_tag_req_vld_m1 & dcu_sbu_tag_req_rdy_m1         |  // 或SBU请求握手成功
                           lsu_dcu_prefetch_valid                                  ;  // 或预取请求有效

assign dcu_pmu_access[1] = lsu_dcu_ld_req_valid_m1[1] & dcu_lsu_ld_req_ready_m1[1] ;  // 端口1加载请求握手成功
 
// 缓存L1读写缺失统计
//  - load0和stc检查命中生成
//  - load1仅在相同缓存行时检查命中生成，utag结果不影响pmu_miss
assign dcu_pmu_miss[0]   = (|dff_tag_type_m2_q) & ~tag_raw_hit;  // 端口0缺失：任何标签类型且未命中
assign dcu_pmu_miss[1]   = dff_tag_type_m2_q[TAG_TYPE_LD] & dff_samecl_m2_q & ~tag_raw_hit;  // 端口1缺失：加载类型且相同缓存行且未命中

//=================================================================================
// RAS Error Report
//=================================================================================

// RAS错误报告端口定义
localparam RAS_PORT_TAG  = 0;  // TAG端口
localparam RAS_PORT_DATA = 1;  // DATA端口
localparam RAS_PORT_AXIB = 2;  // AXIB端口(驱逐缓冲区B通道)

// 传输类型定义
localparam TT_EXPLICIT_READ  = 3'd4;  // 显式读操作
localparam TT_EXPLICIT_WRITE = 3'd5;  // 显式写操作
localparam TT_IMPLICIT_READ  = 3'd6;  // 隐式读操作
localparam TT_IMPLICIT_WRITE = 3'd7;  // 隐式写操作

// 错误代码定义
localparam EC_CACHE_ADDR  = 8'd5 ;  // 缓存地址错误
localparam EC_CACHE_DATA  = 8'd3 ;  // 缓存数据错误
localparam EC_INTERX_DATA = 8'd14;  // 接口数据错误

// 访问类型定义
localparam AEC_LOAD_STORE = 4'd1;  // 加载存储访问
localparam AEC_CMO        = 4'd2;  // CMO(缓存维护操作)访问
localparam AEC_EVICT      = 4'd5;  // 驱逐访问
localparam AEC_DPREFETCH  = 4'd6;  // 数据预取访问

// tag > data load/store > data eviction > B channel error
 
// M3阶段数据ECC错误信号组合
assign data_ecc_sec_m3 = ebank_ecc_sec_m3 | obank_ecc_sec_m3;  // 数据单比特ECC错误(可纠正)
assign data_ecc_ded_m3 = ebank_ecc_ded_m3 | obank_ecc_ded_m3;  // 数据双比特ECC错误(不可纠正)
assign data_ecc_err_m3 = data_ecc_sec_m3  | data_ecc_ded_m3 ;  // 数据ECC错误总信号

// TAG端口RAS错误信号配置
assign dcu_ras_valid   [RAS_PORT_TAG] = tag_ecc_bang_m2;        // TAG ECC错误有效信号
assign dcu_ras_ce      [RAS_PORT_TAG] = tag_ecc_sec_m2 ;        // TAG单比特ECC错误(可纠正)
assign dcu_ras_ued     [RAS_PORT_TAG] = 1'b0;                   // TAG未定义错误(设为0)
assign dcu_ras_uec     [RAS_PORT_TAG] = tag_ecc_ded_m2;         // TAG双比特ECC错误(不可纠正)
assign dcu_ras_priority[RAS_PORT_TAG] = 2'd3;                   // TAG错误优先级最高(3)
assign dcu_ras_tt      [RAS_PORT_TAG] = {4{cmo_arb_req                    }} & TT_EXPLICIT_WRITE  // CMO操作类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_PRF]}} & TT_EXPLICIT_READ   // 预取操作类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_LD ]}} & TT_EXPLICIT_READ   // 加载操作类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_STC]}} & TT_EXPLICIT_WRITE; // 存储操作类型
assign dcu_ras_scrub   [RAS_PORT_TAG] = tag_ecc_sec_m2 & ~tag_ecc_ded_m2;  // TAG擦洗信号(单比特错误时)
// Error code: cache address/control state error
assign dcu_ras_ec      [RAS_PORT_TAG] = EC_CACHE_ADDR;          // 错误代码：缓存地址/控制状态错误
assign dcu_ras_addr    [RAS_PORT_TAG] = dff_addr_m2_q;          // TAG错误地址
assign dcu_ras_aec     [RAS_PORT_TAG] = {4{cmo_arb_req                    }} & AEC_CMO           // CMO访问类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_PRF]}} & AEC_DPREFETCH     // 数据预取访问类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_LD ]}} & AEC_LOAD_STORE    // 加载存储访问类型
                                      | {4{dff_tag_type_m2_q[TAG_TYPE_STC]}} & AEC_LOAD_STORE;   // 加载存储访问类型
assign dcu_ras_tag     [RAS_PORT_TAG] = 1'b1;                   // TAG错误标识
assign dcu_ras_way     [RAS_PORT_TAG] = tag_ecc_ded_m2 ? ram_tag_ecc_ded_m2 : ram_tag_ecc_sec_m2;  // TAG错误路选择


// DATA端口RAS错误信号配置
assign dcu_ras_valid   [RAS_PORT_DATA] = data_ecc_err_m3        // 数据ECC错误有效信号
                                       | eb_ecc_err ;            // 或驱逐缓冲区ECC错误
assign dcu_ras_ce      [RAS_PORT_DATA] = data_ecc_sec_m3 | eb_ecc_sec;  // 数据单比特ECC错误(可纠正)
assign dcu_ras_ued     [RAS_PORT_DATA] = 1'b0;                   // 数据未定义错误(设为0)
assign dcu_ras_uec     [RAS_PORT_DATA] = data_ecc_ded_m3 | eb_ecc_ded;  // 数据双比特ECC错误(不可纠正)
assign dcu_ras_priority[RAS_PORT_DATA] = 2'd1;                   // 数据错误优先级(1)
assign dcu_ras_tt      [RAS_PORT_DATA] = {3{data_ecc_err_m3}} & (dff_std_vld_m3_q ? TT_EXPLICIT_WRITE : TT_EXPLICIT_READ )  // 数据错误操作类型
                                       | {3{eb_ecc_err     }} & (cmo_arb_req      ? TT_EXPLICIT_WRITE : TT_IMPLICIT_WRITE); // 驱逐缓冲区错误操作类型
assign dcu_ras_scrub   [RAS_PORT_DATA] = data_ecc_sec_m3 & ~data_ecc_ded_m3;  // 数据擦洗信号(单比特错误时)
assign dcu_ras_ec      [RAS_PORT_DATA] = EC_CACHE_DATA;          // 错误代码：缓存数据错误
assign dcu_ras_addr    [RAS_PORT_DATA] = data_ecc_ded_m3 ? (ebank_ecc_ded_m3 ? ecc_ebank_addr : ecc_obank_addr) :  // 双比特错误地址选择
                                         data_ecc_sec_m3 ? (ebank_ecc_sec_m3 ? ecc_ebank_addr : ecc_obank_addr) :  // 单比特错误地址选择
                                                           eb_w_mux_addr                                        ;  // 驱逐缓冲区地址
assign dcu_ras_aec     [RAS_PORT_DATA] = {4{data_ecc_err_m3}} & AEC_LOAD_STORE    // 数据错误访问类型
                                       | {4{eb_ecc_err     }} & (cmo_arb_req ? AEC_CMO : AEC_EVICT);  // 驱逐缓冲区错误访问类型
assign dcu_ras_tag     [RAS_PORT_DATA] = 1'b0;                   // 数据错误标识
assign dcu_ras_way     [RAS_PORT_DATA] = data_ecc_ded_m3 ? (ebank_ecc_ded_m3 ? ebank_ecc_pair_m3 : obank_ecc_pair_m3)  // 双比特错误路选择
                                                         : (ebank_ecc_sec_m3 ? ebank_ecc_pair_m3 : obank_ecc_pair_m3); // 单比特错误路选择

// AXIB端口RAS错误信号配置(驱逐缓冲区B通道错误)
assign dcu_ras_valid   [RAS_PORT_AXIB] = eb_b_err;              // 驱逐缓冲区B通道错误有效信号
assign dcu_ras_ce      [RAS_PORT_AXIB] = 1'b0;                   // 驱逐缓冲区B通道单比特错误(设为0)
assign dcu_ras_ued     [RAS_PORT_AXIB] = 1'b0;                   // 驱逐缓冲区B通道未定义错误(设为0)
assign dcu_ras_uec     [RAS_PORT_AXIB] = eb_b_err;               // 驱逐缓冲区B通道双比特错误(不可纠正)
assign dcu_ras_priority[RAS_PORT_AXIB] = 2'd3;                   // 驱逐缓冲区B通道错误优先级最高(3)
assign dcu_ras_tt      [RAS_PORT_AXIB] = TT_IMPLICIT_WRITE;      // 驱逐缓冲区B通道操作类型(隐式写)
assign dcu_ras_scrub   [RAS_PORT_AXIB] = 1'b0;                   // 驱逐缓冲区B通道擦洗信号(设为0)
assign dcu_ras_ec      [RAS_PORT_AXIB] = EC_INTERX_DATA;         // 错误代码：接口数据错误
assign dcu_ras_addr    [RAS_PORT_AXIB] = eb_b_addr;              // 驱逐缓冲区B通道错误地址
assign dcu_ras_aec     [RAS_PORT_AXIB] = AEC_EVICT;              // 驱逐缓冲区B通道访问类型(驱逐)
assign dcu_ras_tag     [RAS_PORT_AXIB] = 1'b0;                   // 驱逐缓冲区B通道错误标识
assign dcu_ras_way     [RAS_PORT_AXIB] = {WAY_N{1'b0}};          // 驱逐缓冲区B通道路选择(全0)


//=================================================================================
// Assertion
//=================================================================================



//**********************************************************************************************//
//**********************************************************************************************//
`else // CHUNJUN_SUPPORT_DCACHE
//**********************************************************************************************//
//**********************************************************************************************//

//=================================================================================
// Local Variables 
//=================================================================================

// 数据缓存地址读取信息结构体定义
typedef struct packed {
  logic [32-1:0]    addr  ;  // 地址(32位)
  logic [1:0]                  size  ;  // 传输大小(2位)
  logic                        idemp ;  // 幂等性标志
  logic [2:0]                  oma   ;  // 外部内存属性(3位)
  logic                        excl  ;  // 排他访问标志
  logic                        priv  ;  // 特权访问标志
  logic                        master;  // 主设备标志
} dc_ar_info_t;

// 流水线M2阶段寄存器信号
logic                                              dff_ppln_vld_m2_set;  // 流水线有效位设置
logic                                              dff_ppln_vld_m2_clr;  // 流水线有效位清除
logic                                              dff_ppln_vld_m2_en ;  // 流水线有效位使能
logic                                              dff_ppln_vld_m2_d  ;  // 流水线有效位数据输入
logic                                              dff_ppln_vld_m2_q  ;  // 流水线有效位数据输出
logic                                              dff_ppln_info_m2_en;  // 流水线信息使能
dc_ar_info_t                                       dff_ppln_info_m2_d ;  // 流水线信息数据输入
dc_ar_info_t                                       dff_ppln_info_m2_q ;  // 流水线信息数据输出
logic         [LVQID_W-1:0]                        dff_ppln_id_m2_d;    // 流水线ID数据输入
logic         [LVQID_W-1:0]                        dff_ppln_id_m2_q;    // 流水线ID数据输出
 
// 缺失缓冲区(MB)寄存器信号
logic         [MB_ENT_N-1:0]                       dff_mb_vld_set ;  // MB有效位设置
logic         [MB_ENT_N-1:0]                       dff_mb_vld_clr ;  // MB有效位清除
logic         [MB_ENT_N-1:0]                       dff_mb_vld_en  ;  // MB有效位使能
logic         [MB_ENT_N-1:0]                       dff_mb_vld_d   ;  // MB有效位数据输入
logic         [MB_ENT_N-1:0]                       dff_mb_vld_q   ;  // MB有效位数据输出
logic         [MB_ENT_N-1:0]                       dff_mb_sent_set;  // MB发送位设置
logic         [MB_ENT_N-1:0]                       dff_mb_sent_clr;  // MB发送位清除
logic         [MB_ENT_N-1:0]                       dff_mb_sent_en ;  // MB发送位使能
logic         [MB_ENT_N-1:0]                       dff_mb_sent_d  ;  // MB发送位数据输入
logic         [MB_ENT_N-1:0]                       dff_mb_sent_q  ;  // MB发送位数据输出
logic         [MB_ENT_N-1:0]                       mb_ar_vld_vec       ;  // MB地址读取有效向量
logic         [MB_ENT_N-1:0]                       mb_ar_vld_oldest_oh ;  // MB最旧地址读取有效one-hot
logic         [MB_ENT_N-1:0]                       dff_mb_flushed_set;  // MB刷新位设置
logic         [MB_ENT_N-1:0]                       dff_mb_flushed_clr;  // MB刷新位清除
logic         [MB_ENT_N-1:0]                       dff_mb_flushed_en ;  // MB刷新位使能
logic         [MB_ENT_N-1:0]                       dff_mb_flushed_d  ;  // MB刷新位数据输入
logic         [MB_ENT_N-1:0]                       dff_mb_flushed_q  ;  // MB刷新位数据输出
logic         [MB_ENT_N-1:0]                       dff_mb_info_en;     // MB信息使能
dc_ar_info_t                                       dff_mb_info_d ;     // MB信息数据输入
dc_ar_info_t  [MB_ENT_N-1:0]                       dff_mb_info_q ;     // MB信息数据输出数组
dc_ar_info_t                                       mb_ar_info    ;     // MB地址读取信息
logic         [MB_ENT_N-1:0][LD_OFFS_W-1:0]        mb_ld_offs    ;     // MB加载偏移数组
logic         [LVQID_W-1:0]                        dff_mb_id_d;        // MB ID数据输入
logic         [MB_ENT_N-1:0][LVQID_W-1:0]          dff_mb_id_q;        // MB ID数据输出数组
 
// 加载0响应实体信号
logic                                              ld0_rsp_ent_flushed;  // 加载0响应实体已刷新
logic         [LD_OFFS_W-1:0]                      ld0_rsp_ent_ld_offs;  // 加载0响应实体加载偏移
logic         [LVQID_W-1:0]                        ld0_rsp_ent_id;       // 加载0响应实体ID
logic                                              ld0_rsp_ent_src;      // 加载0响应实体源
logic         [31:0]                               ld0_rsp_data;         // 加载0响应数据

// 缺失缓冲区控制信号
logic                                              flush_ppln_m2;  // 刷新流水线M2
logic                                              mb_push_en;     // MB推入使能
logic                                              mb_pop_en ;     // MB弹出使能
logic                                              mb_full   ;     // MB满标志
logic                                              mb_empty  ;     // MB空标志
logic         [MB_ID_W-1:0]                        mb_push_idx;    // MB推入索引
logic         [MB_ID_W-1:0]                        mb_pop_idx ;    // MB弹出索引
logic         [MB_ID_W-1:0]                        mb_ar_idx  ;    // MB地址读取索引

// 加载请求信号
logic         [31:0]                               ld_req_addr_m1       ;  // 加载请求地址M1
logic         [1:0]                                ld_req_size_m1       ;  // 加载请求大小M1
logic         [LVQID_W-1:0]                        ld_lsqid_m1          ;  // 加载LSQ ID M1
logic                                              ld_req_idempotency_m1;  // 加载请求幂等性M1
logic         [2:0]                                ld_req_outer_ma_m1   ;  // 加载请求外部内存属性M1
logic                                              ld_req_master_m1     ;  // 加载请求主设备M1
logic                                              ld_req_excl_m1       ;  // 加载请求排他M1
logic                                              ld_req_hdsk          ;  // 加载请求握手
logic                                              ld_src_m2            ;  // 加载源M2
logic                                              ld_cancel_m2         ;  // 加载取消M2
logic         [MB_ENT_N-1:0]                       mb_src               ;  // MB源
logic                                              dff_remap_id_en      ;  // 重映射ID使能
logic [MB_ID_W-1:0]                                dff_remap_id_d       ;  // 重映射ID数据输入
logic [MB_ID_W-1:0]                                dff_remap_id_q       ;  // 重映射ID数据输出

//=================================================================================
// VPU Process
//=================================================================================

// VPU and LSU can NOT send request to DCU simultaneously.
// Hence, no arbiter needed.


`ifdef CHUNJUN_SUPPORT_V_EXT


logic                                              dff_ld_src_m2_en;
logic                                              dff_ld_src_m2_d ;
logic                                              dff_ld_src_m2_q ;
logic                                              dff_mb_src_d;
logic         [MB_ENT_N-1:0]                       dff_mb_src_q;

assign ld_cancel_m2          = lsu_dcu_ld_req_cancel_m2[0] | vpu_dcu_ld_req_cancel_m2[0]; //这意味着无论请求来自LSU还是VPU，只要其中一个发出取消指令，该加载操作就应被取消。
 //如果VPU的加载请求有效（vpu_dcu_ld_req_vld_m1[0]为真），则选择VPU的参数；
assign ld_req_addr_m1        = vpu_dcu_ld_req_vld_m1[0] ? vpu_dcu_ld_req_addr_m1       [0] : lsu_dcu_ld_req_addr_m1       [0];//当vpu的加载请求有效时，选择VPU的地址，否则选择LSU的地址
assign ld_req_size_m1        = vpu_dcu_ld_req_vld_m1[0] ? vpu_dcu_ld_req_size_m1       [0] : lsu_dcu_ld_req_size_m1       [0];//当vpu的加载请求有效时，选择VPU的size，否则选择LSU的size
assign ld_req_idempotency_m1 = vpu_dcu_ld_req_vld_m1[0] ? vpu_dcu_ld_req_idempotency_m1[0] : lsu_dcu_ld_req_idempotency_m1[0];//当vpu的加载请求有效时，选择VPU的idempotency，否则选择LSU的idempotency
assign ld_req_outer_ma_m1    = vpu_dcu_ld_req_vld_m1[0] ? vpu_dcu_ld_req_outer_ma_m1   [0] : lsu_dcu_ld_req_outer_ma_m1   [0];//当vpu的加载请求有效时，选择VPU的outer_ma，否则选择LSU的outer_ma
assign ld_req_excl_m1        = vpu_dcu_ld_req_vld_m1[0] ? 1'b0                             : lsu_dcu_ld_req_excl_m1       [0];//当vpu的加载请求有效时，选择VPU的excl，否则选择LSU的excl
assign ld_req_master_m1      = vpu_dcu_ld_req_vld_m1[0] ? vpu_dcu_ld_req_master_m1     [0] : lsu_dcu_ld_req_master_m1     [0];//当vpu的加载请求有效时，选择VPU的master，否则选择LSU的master
 //将VPU的VLQID（Vector Load Queue ID）进行零填充，使其宽度达到LVQID_W。
assign ld_lsqid_m1           = vpu_dcu_ld_req_vld_m1[0] ? { {LVQID_W-VLQID_W{1'b0}}, vpu_dcu_ld_req_vlqid_m1[0]} //当vpu的加载请求有效时，选择VPU的VLQID，否则选择LSU的VLQID
                                                        : { {LVQID_W-LSQID_W{1'b0}}, lsu_dcu_ld_req_lsqid_m1[0]}; 
 
assign ld_req_hdsk = lsu_dcu_ld_req_valid_m1[0] & dcu_lsu_ld_req_ready_m1[0] | //vpn shake or lsu shake
                     vpu_dcu_ld_req_vld_m1  [0] & dcu_vpu_ld_req_rdy_m1  [0] ;

assign dff_ld_src_m2_en = ld_req_hdsk;
assign dff_ld_src_m2_d  = lsu_dcu_ld_req_valid_m1[0]; // 0: ld request from VPU, 1: ld request from LSU
 
`WDFFER(dff_ld_src_m2_q, dff_ld_src_m2_d, dff_ld_src_m2_en, clk, rst_n)

// 0: ld request from VPU
// 1: ld request from LSU
assign ld_src_m2 = dff_ld_src_m2_q; //0: ld request from VPU, 1: ld request from LSU

assign dff_mb_src_d = ld_src_m2;
 
for (genvar i=0; i<MB_ENT_N; i++) begin : g_mb_src
  `WDFFER(dff_mb_src_q[i], dff_mb_src_d, dff_mb_info_en[i], clk, rst_n)
end

assign mb_src = dff_mb_src_q;

wing_cbb_aomux_idx #(.NUM(MB_ENT_N), .WIDTH(1)) u_aomux_mb_src(.data_arr(mb_src), .sel_idx(mb_pop_idx), .data_sel(ld0_rsp_ent_src));


`else   // CHUNJUN_SUPPORT_V_EXT


assign ld_req_addr_m1        = lsu_dcu_ld_req_addr_m1       [0];
assign ld_req_size_m1        = lsu_dcu_ld_req_size_m1       [0];
assign ld_req_idempotency_m1 = lsu_dcu_ld_req_idempotency_m1[0];
assign ld_req_outer_ma_m1    = lsu_dcu_ld_req_outer_ma_m1   [0];
assign ld_req_excl_m1        = lsu_dcu_ld_req_excl_m1       [0];
assign ld_req_master_m1      = lsu_dcu_ld_req_master_m1     [0];
assign ld_lsqid_m1           = lsu_dcu_ld_req_lsqid_m1      [0];

assign ld_req_hdsk = lsu_dcu_ld_req_valid_m1[0] & dcu_lsu_ld_req_ready_m1[0];
 
// 0: ld request from VPU
// 1: ld request from LSU
assign ld_src_m2        = 1'b1;
 
assign ld_cancel_m2     = lsu_dcu_ld_req_cancel_m2[0];
 
assign mb_src           = {MB_ENT_N{1'b1}};
assign ld0_rsp_ent_src  = 1'b1;

`endif  // CHUNJUN_SUPPORT_V_EXT

//=================================================================================
// Pipeline Register
//=================================================================================

// M1 -> M2

assign dff_ppln_vld_m2_set = ld_req_hdsk;
assign dff_ppln_vld_m2_clr = dff_ppln_vld_m2_q;
assign dff_ppln_vld_m2_en  = dff_ppln_vld_m2_set | dff_ppln_vld_m2_clr;
assign dff_ppln_vld_m2_d   = dff_ppln_vld_m2_set;

assign dff_ppln_info_m2_en       = ld_req_hdsk;
 
assign dff_ppln_info_m2_d.addr   = ld_req_addr_m1       ;
assign dff_ppln_info_m2_d.size   = ld_req_size_m1       ;
assign dff_ppln_info_m2_d.idemp  = ld_req_idempotency_m1;
assign dff_ppln_info_m2_d.oma    = ld_req_outer_ma_m1   ;
assign dff_ppln_info_m2_d.excl   = ld_req_excl_m1       ;
assign dff_ppln_info_m2_d.priv   = |core_priv_mode;
assign dff_ppln_info_m2_d.master = ld_req_master_m1     ;
 
assign dff_ppln_id_m2_d      = ld_lsqid_m1;

`WDFFER(dff_ppln_vld_m2_q  , dff_ppln_vld_m2_d , dff_ppln_vld_m2_en , clk, rst_n)
`WDFFER(dff_ppln_info_m2_q , dff_ppln_info_m2_d, dff_ppln_info_m2_en, clk, rst_n)
`WDFFER(dff_ppln_id_m2_q   , dff_ppln_id_m2_d  , dff_ppln_info_m2_en, clk, rst_n)

//=================================================================================
// Miss Buffer
//=================================================================================

// Miss_Buffer is based on age matrix.

// Distinguish flush really from LSU since LSU forward flush to DCU once LSU get flush from PCU.
assign flush_ppln_m2 = lsu_dcu_flush_valid & id_younger(dff_ppln_id_m2_q[LSQID_W-1:0], lsu_dcu_flush_lsqid) & ld_src_m2;
 
assign mb_push_en = dff_ppln_vld_m2_q & ~mb_full & ~ld_cancel_m2 & ~flush_ppln_m2;

// DIRECTION==0: trailing zero count.
wing_cbb_lzc #(
    .WIDTH     ( MB_ENT_N ),
    .DIRECTION ( 0 ),
    .INVERT    ( 1 )
) u_dcmb_lzc (
    .vec_i   ( dff_mb_vld_q ),
    .cnt_o   ( mb_push_idx  ),
    .empty_o (              )
);

assign mb_pop_en  = bmu_dcu_r_vld;
assign mb_pop_idx = ((bmu_dcu_r_id == AXI_ID_EXCLD) || (bmu_dcu_r_id == AXI_ID_DVCLD)) ? dff_remap_id_q 
                                                                                       : bmu_dcu_r_id[MB_ID_W-1:0];

assign mb_full  =  &dff_mb_vld_q;
assign mb_empty = ~|dff_mb_vld_q;

// Miss_Buffer info, only include constant info once pushed into FIFO.
assign dff_mb_info_d.addr   = dff_ppln_info_m2_q.addr  ;
assign dff_mb_info_d.size   = dff_ppln_info_m2_q.size  ;
assign dff_mb_info_d.idemp  = (lsu_dcu_ld_req_ma_update_m2[0] & ld_src_m2) ? lsu_dcu_ld_req_idempotency_m2[0] : dff_ppln_info_m2_q.idemp;
assign dff_mb_info_d.oma    = (lsu_dcu_ld_req_ma_update_m2[0] & ld_src_m2) ? lsu_dcu_ld_req_outer_ma_m2   [0] : dff_ppln_info_m2_q.oma;
assign dff_mb_info_d.excl   = dff_ppln_info_m2_q.excl  ;
assign dff_mb_info_d.priv   = dff_ppln_info_m2_q.priv  ;
assign dff_mb_info_d.master = dff_ppln_info_m2_q.master;

assign dff_mb_id_d      = dff_ppln_id_m2_q ;

for (genvar i=0; i<MB_ENT_N; i++) begin : g_mb_info
  assign dff_mb_vld_set[i] = mb_push_en & (i==mb_push_idx);
  assign dff_mb_vld_clr[i] = (mb_pop_en & (i==mb_pop_idx )) | 
                             (dff_mb_flushed_set[i] & ~(dff_mb_sent_q[i] | mb_ar_vld_oldest_oh[i]));
  assign dff_mb_vld_en [i] = dff_mb_vld_set[i] | dff_mb_vld_clr[i];
  assign dff_mb_vld_d  [i] = dff_mb_vld_set[i];

  `WDFFER(dff_mb_vld_q[i], dff_mb_vld_d[i], dff_mb_vld_en[i], clk, rst_n)
 
  assign dff_mb_info_en[i] = dff_mb_vld_set[i];

  `WDFFER(dff_mb_info_q[i], dff_mb_info_d, dff_mb_info_en[i], clk, rst_n)
  `WDFFER(dff_mb_id_q  [i], dff_mb_id_d  , dff_mb_info_en[i], clk, rst_n)

  assign mb_ld_offs[i] = dff_mb_info_q[i].addr[2 +: LD_OFFS_W];
 
  assign dff_mb_sent_set[i] = dcu_bmu_ar_vld & bmu_dcu_ar_rdy & mb_ar_vld_oldest_oh[i];
  assign dff_mb_sent_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_sent_en [i] = dff_mb_sent_set[i] | dff_mb_sent_clr[i];
  assign dff_mb_sent_d  [i] = dff_mb_sent_set[i];
 
  `WDFFER(dff_mb_sent_q[i], dff_mb_sent_d[i], dff_mb_sent_en[i], clk, rst_n)

  assign mb_ar_vld_vec[i] = dff_mb_vld_q[i] & ~dff_mb_sent_q[i];
 
  assign dff_mb_flushed_set[i] = dff_mb_vld_q[i] & mb_src[i] & 
                                 lsu_dcu_flush_valid & id_younger(dff_mb_id_q[i][LSQID_W-1:0], lsu_dcu_flush_lsqid);
  assign dff_mb_flushed_clr[i] = dff_mb_vld_clr[i];
  assign dff_mb_flushed_en [i] = dff_mb_flushed_set[i] | dff_mb_flushed_clr[i];
  assign dff_mb_flushed_d  [i] = dff_mb_flushed_set[i] & ~dff_mb_flushed_clr[i];
 
  `WDFFER(dff_mb_flushed_q[i], dff_mb_flushed_d[i], dff_mb_flushed_en[i], clk, rst_n)

end
 
 wing_cbb_age_matrix #(
  .ENTRY_NUM       ( MB_ENT_N ),
  .WR_NUM          ( 1        ),
  .FIND_OLDEST     ( 1        ),
  .RD_NUM_OLD      ( 1        ),
  .FIND_NEWEST     ( 0        ),
  .RD_NUM_NEW      ( 1        ),
  .VLD_VEC_NUM_OLD ( 1        ),
  .VLD_VEC_NUM_NEW ( 1        )
) u_dcmb_age_matrix (
    .clk            ( clk                 ),
    .rst_n          ( rst_n               ),
    .wr_en          ( mb_push_en          ),
    .wr_id          ( mb_push_idx         ),
    .oldest_rd_oh   ( mb_ar_vld_oldest_oh ),
    .newest_rd_oh   (                     ),
    .oldest_vld_vec ( mb_ar_vld_vec       ),
    .newest_vld_vec ( {MB_ENT_N{1'b0}}    )
);

wing_cbb_aomux #(.NUM(MB_ENT_N), .T(dc_ar_info_t)) u_aomux_arinfo (.data_arr(dff_mb_info_q), .sel_in_onehot0(mb_ar_vld_oldest_oh), .data_sel(mb_ar_info));

wing_cbb_onehot2bin #(.WIDTH(MB_ENT_N)) u_oh2binmbid (.onehot_i(mb_ar_vld_oldest_oh), .bin_o(mb_ar_idx));

//=================================================================================
// Interface
//=================================================================================

assign dcu_init_busy = 1'b0;
assign dcu_idle      = mb_empty;
 
assign dcu_lsu_ld_req_ready_m1[0] = ~mb_full;
assign dcu_lsu_ld_req_ready_m1[1] = 1'b0   ;

assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_MBF] = ld_src_m2 & dff_ppln_vld_m2_q & mb_full;
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_AWL] = 1'b0;
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_MHM] = 1'b0;
assign dcu_lsu_ld_replay_cause_m2[0][REPLAY_AWU] = 1'b0;
assign dcu_lsu_ld_replay_lsqid_m2[0]             = dff_ppln_id_m2_q[LSQID_W-1:0];
 
assign dcu_lsu_ld_replay_cause_m2[1] = 4'b0;
assign dcu_lsu_ld_replay_lsqid_m2[1] = {LSQID_W{1'b0}};

assign dcu_lsu_ld_replay_mbid_m2  = {2*MB_ID_W{1'b0}};
assign dcu_lsu_mb_rls_array       = {4{1'b0}};

wing_cbb_aomux_idx #(.NUM(MB_ENT_N), .WIDTH(1        )) u_aomux_mb_flushed(.data_arr(dff_mb_flushed_q), .sel_idx(mb_pop_idx), .data_sel(ld0_rsp_ent_flushed));
wing_cbb_aomux_idx #(.NUM(MB_ENT_N), .WIDTH(LD_OFFS_W)) u_aomux_mb_ld_offs(.data_arr(mb_ld_offs      ), .sel_idx(mb_pop_idx), .data_sel(ld0_rsp_ent_ld_offs));
wing_cbb_aomux_idx #(.NUM(MB_ENT_N), .WIDTH(LVQID_W  )) u_aomux_mb_id     (.data_arr(dff_mb_id_q     ), .sel_idx(mb_pop_idx), .data_sel(ld0_rsp_ent_id));

wing_cbb_aomux_idx #(
  .NUM   ( MBUS_WORD_N ),
  .WIDTH ( 32          )
) u_aomux_ld0data (
  .data_arr ( bmu_dcu_r_data      ),
  .sel_idx  ( ld0_rsp_ent_ld_offs ),
  .data_sel ( ld0_rsp_data        )
);

assign dcu_lsu_ld_rsp_valid_m2    [0] = bmu_dcu_r_vld & ~ld0_rsp_ent_flushed & ld0_rsp_ent_src;//当BMU(总线管理单元)返回有效数据且请求未被刷新且来自有效源时，设置响应有效
assign dcu_lsu_ld_rsp_buserr_m2   [0] = bmu_dcu_r_resp[1];//总线错误信号，来自BMU响应的第1位
assign dcu_lsu_ld_rsp_excl_okay_m2[0] = bmu_dcu_r_resp[0];//排他加载成功信号，来自BMU响应的第0位
assign dcu_lsu_ld_rsp_lsqid_m2    [0] = ld0_rsp_ent_id[LSQID_W-1:0];//加载存储队列ID，用于标识对应的加载请求
assign dcu_lsu_ld_rsp_data_m2     [0] = ld0_rsp_data;

assign dcu_lsu_ld_rsp_valid_m2    [1] = 1'b0;
assign dcu_lsu_ld_rsp_data_m2     [1] = 32'b0;
assign dcu_lsu_ld_rsp_lsqid_m2    [1] = {LSQID_W{1'b0}};
assign dcu_lsu_ld_rsp_buserr_m2   [1] = 1'b0;
assign dcu_lsu_ld_rsp_excl_okay_m2[1] = 1'b0;
 
assign dcu_lsu_ld_rsp_valid_m3     = 2'b0;
assign dcu_lsu_ld_rsp_raserr_m3    = 2'b0;
assign dcu_lsu_ld_rsp_replay_m3    = 2'b0;
assign dcu_lsu_ld_rsp_lsqid_m3     = {2*LSQID_W{1'b0}};


`ifdef CHUNJUN_SUPPORT_V_EXT
 
assign dcu_vpu_ld_req_rdy_m1[0] = ~mb_full;
assign dcu_vpu_ld_req_rdy_m1[1] = 1'b0    ;

assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_MBF] = ~ld_src_m2 & dff_ppln_vld_m2_q & mb_full;
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_AWL] = 1'b0;
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_MHM] = 1'b0;
assign dcu_vpu_ld_replay_cause_m2[0][REPLAY_AWU] = 1'b0;
assign dcu_vpu_ld_replay_vlqid_m2[0]             = dff_ppln_id_m2_q[VLQID_W-1:0];
 
assign dcu_vpu_ld_replay_cause_m2[1] = 4'b0;
assign dcu_vpu_ld_replay_vlqid_m2[1] = {VLQID_W{1'b0}};

assign dcu_vpu_ld_replay_mbid_m2 = {2*MB_ID_W{1'b0}};
assign dcu_vpu_mb_rls_array      = {4{1'b0}};
 
assign dcu_vpu_ld_rsp_vld_m2   [0] = bmu_dcu_r_vld & ~ld0_rsp_ent_src;
assign dcu_vpu_ld_rsp_data_m2  [0] = ld0_rsp_data;
assign dcu_vpu_ld_rsp_vlqid_m2 [0] = ld0_rsp_ent_id[VLQID_W-1:0];
assign dcu_vpu_ld_rsp_buserr_m2[0] = bmu_dcu_r_resp[1];
 
assign dcu_vpu_ld_rsp_vld_m2   [1] = 1'b0;
assign dcu_vpu_ld_rsp_data_m2  [1] = 32'b0;
assign dcu_vpu_ld_rsp_vlqid_m2 [1] = {VLQID_W{1'b0}};
assign dcu_vpu_ld_rsp_buserr_m2[1] = 1'b0;
 
assign dcu_vpu_ld_rsp_vld_m3     = 2'b0;
assign dcu_vpu_ld_rsp_raserr_m3  = 2'b0;
assign dcu_vpu_ld_rsp_replay_m3  = 2'b0;
assign dcu_vpu_ld_rsp_vlqid_m3   = {2*VLQID_W{1'b0}};
 
`endif  // CHUNJUN_SUPPORT_V_EXT


assign dcu_sbu_tag_req_rdy_m1      = 1'b0;  // DCU到SBU标签请求就绪M1(禁用)
assign dcu_sbu_tag_rsp_vld_m2      = 1'b0;  // DCU到SBU标签响应有效M2(禁用)
assign dcu_sbu_tag_rsp_hit_m2      = 1'b0;  // DCU到SBU标签响应命中M2(禁用)
assign dcu_sbu_tag_rsp_status_m2   = 1'b0;  // DCU到SBU标签响应状态M2(禁用)
assign dcu_sbu_tag_rsp_way_m2      = {WAYIDX_W{1'b0}};  // DCU到SBU标签响应路M2(清零)
assign dcu_sbu_tag_rsp_sbqid_m2    = {SBQID_W{1'b0}};  // DCU到SBU标签响应SBQID M2(清零)
assign dcu_sbu_tag_rsp_excl_ok_m2  = 1'b0;  // DCU到SBU标签响应独占OK M2(禁用)
assign dcu_sbu_tag_rsp_raserr_m2   = 1'b0;  // DCU到SBU标签响应RAS错误M2(禁用)
assign dcu_sbu_tag_replay_cause_m2 = 5'b0;  // DCU到SBU标签重放原因M2(清零)
assign dcu_sbu_tag_replay_mbid_m2  = {MB_ID_W{1'b0}};  // DCU到SBU标签重放MBID M2(清零)
assign dcu_sbu_cmo_done            = 1'b0;  // DCU到SBU缓存管理操作完成(禁用)

assign dcu_sbu_filldone            = 1'b0;  // DCU到SBU填充完成(禁用)
assign dcu_sbu_filldone_err        = 1'b0;  // DCU到SBU填充完成错误(禁用)
assign dcu_sbu_filldone_sbqid      = {SBQID_W{1'b0}};  // DCU到SBU填充完成SBQID(清零)

assign dcu_sbu_mb_rls_array        = {4{1'b0}};  // DCU到SBU缺失缓冲区释放数组(清零)
assign dcu_sbu_eb_done_array       = {4{1'b0}};  // DCU到SBU驱逐缓冲区完成数组(清零)
 
assign dcu_sbu_data_req_rdy_m1     = 1'b0;  // DCU到SBU数据请求就绪M1(禁用)
assign dcu_sbu_data_rsp_vld_m3     = 1'b0;  // DCU到SBU数据响应有效M3(禁用)
assign dcu_sbu_data_rsp_rdata_m3   = 64'b0;  // DCU到SBU数据响应读取数据M3(清零)
assign dcu_sbu_data_rsp_raserr_m3  = 1'b0;  // DCU到SBU数据响应RAS错误M3(禁用)
assign dcu_sbu_data_rsp_sbqid_m3   = {SBQID_W{1'b0}};  // DCU到SBU数据响应SBQID M3(清零)

assign dcu_bmu_ar_vld   = |mb_ar_vld_oldest_oh;  // DCU到BMU地址读有效(最老的缺失缓冲区地址读有效)

assign dcu_bmu_ar_id    = mb_ar_info.excl   ? AXI_ID_EXCLD :  // DCU到BMU地址读ID(独占加载使用独占ID)
                          ~mb_ar_info.idemp ? AXI_ID_DVCLD :  // 非幂等加载使用设备ID
                            {1'b1, {4-1-MB_ID_W{1'b0}}, mb_ar_idx};  // 其他情况使用MB索引
 
assign dff_remap_id_en = dcu_bmu_ar_vld & bmu_dcu_ar_rdy &   // 重映射ID使能(DCU地址读有效且BMU地址读就绪且需要重映射)
                         (mb_ar_info.excl | ~mb_ar_info.idemp);  // 独占或非幂等操作需要重映射
assign dff_remap_id_d  = mb_ar_idx;  // 重映射ID数据输入(MB地址读索引)
 
`WDFFER(dff_remap_id_q, dff_remap_id_d, dff_remap_id_en, clk, rst_n)  // 重映射ID寄存器

assign dcu_bmu_ar_addr  = mb_ar_info.addr;  // DCU到BMU地址读地址(MB地址读信息地址)
assign dcu_bmu_ar_size  = {1'b0, mb_ar_info.size};  // DCU到BMU地址读大小(MB地址读信息大小)
assign dcu_bmu_ar_len   = 8'b0;  // DCU到BMU地址读长度(固定为0，单次传输)
assign dcu_bmu_ar_burst = 2'b01;  // DCU到BMU地址读突发类型(固定为INCR)
assign dcu_bmu_ar_lock  = mb_ar_info.excl;  // DCU到BMU地址读锁定(独占操作时锁定)
assign dcu_bmu_ar_cache = ~mb_ar_info.idemp         ? AXI_CACHE_DEVICE :  // DCU到BMU地址读缓存属性(非幂等使用设备缓存)
                          ~mb_ar_info.oma[MA_CACHE] ? AXI_CACHE_NC     :  // 非缓存使用NC
                                                      {1'b1, mb_ar_info.oma[1], 2'b11};  // 其他使用缓存属性
assign dcu_bmu_ar_prot  = {2'b00, mb_ar_info.priv};  // DCU到BMU地址读保护(特权位)
assign dcu_bmu_ar_master = 1'b0;  // DCU到BMU地址读主控(固定为0)

assign dcu_bmu_r_rdy    = 1'b1;  // DCU到BMU读响应就绪(始终就绪)
assign dcu_bmu_b_rdy    = 1'b1;  // DCU到BMU写响应就绪(始终就绪)
 
assign dcu_bmu_aw_vld   = 1'b0;  // DCU到BMU地址写有效(禁用)
assign dcu_bmu_aw_id    = {4{1'b0}};  // DCU到BMU地址写ID(清零)
assign dcu_bmu_aw_addr  = {32{1'b0}};  // DCU到BMU地址写地址(清零)
assign dcu_bmu_aw_size  = 3'b0;  // DCU到BMU地址写大小(清零)
assign dcu_bmu_aw_len   = 8'b0;  // DCU到BMU地址写长度(清零)
assign dcu_bmu_aw_burst = 2'b0;  // DCU到BMU地址写突发类型(清零)
assign dcu_bmu_aw_lock  = 1'b0;  // DCU到BMU地址写锁定(清零)
assign dcu_bmu_aw_cache = 4'b0;  // DCU到BMU地址写缓存属性(清零)
assign dcu_bmu_aw_prot  = 3'b0;  // DCU到BMU地址写保护(清零)
assign dcu_bmu_aw_master = 1'b0;  // DCU到BMU地址写主控(清零)
assign dcu_bmu_aw_atop  = 6'b0;  // DCU到BMU地址写原子操作(清零)
assign dcu_bmu_w_vld    = 1'b0;  // DCU到BMU写数据有效(禁用)
assign dcu_bmu_w_data   = {`CHUNJUN_MBUS_DATA_W{1'b0}};  // DCU到BMU写数据(清零)
assign dcu_bmu_w_strb   = {`CHUNJUN_MBUS_DATA_W/8{1'b0}};  // DCU到BMU写选通(清零)
assign dcu_bmu_w_last   = 1'b0;  // DCU到BMU写最后(清零)

assign dcu_ras_valid    = {3{1'b0}};  // DCU RAS有效(清零)
assign dcu_ras_ce       = {3{1'b0}};  // DCU RAS可纠正错误(清零)
assign dcu_ras_ued      = {3{1'b0}};  // DCU RAS不可纠正错误检测(清零)
assign dcu_ras_uec      = {3{1'b0}};  // DCU RAS不可纠正错误纠正(清零)
assign dcu_ras_priority = {3{2'b0}};  // DCU RAS优先级(清零)
assign dcu_ras_tt       = {3{3'b0}};  // DCU RAS事务类型(清零)
assign dcu_ras_scrub    = {3{1'b0}};  // DCU RAS擦洗(清零)
assign dcu_ras_ec       = {3{8'b0}};  // DCU RAS错误代码(清零)
assign dcu_ras_addr     = {3{32'b0}};  // DCU RAS地址(清零)
assign dcu_ras_aec      = {3{4'b0}};  // DCU RAS访问错误代码(清零)
assign dcu_ras_tag      = {3{1'b0}};  // DCU RAS标签(清零)
assign dcu_ras_way      = {3{4'b0}};  // DCU RAS路(清零)

assign dcu_pmu_access   = |(lsu_dcu_ld_req_valid_m1 & dcu_lsu_ld_req_ready_m1);  // DCU PMU访问(任何LSU加载请求有效且DCU就绪)
assign dcu_pmu_miss     = 1'b0;  // DCU PMU缺失(禁用)

//**********************************************************************************************//
//**********************************************************************************************//
`endif  // CHUNJUN_SUPPORT_DCACHE
//**********************************************************************************************//
//**********************************************************************************************//

endmodule

`include "chunjun_undefine.sv" 
